PL λαβ

Lab 3.1a[최준혁]: 람다와 클로저 본문

kos

Lab 3.1a[최준혁]: 람다와 클로저

junhyeokk 2021. 4. 27. 21:26

Code

모듈로 나누어서 작업을 하다가 모듈끼리 circular import 문제가 생겨, 조교님이 작성해주신 boilerplate 코드와 아래 코드, 두 모듈로 이전의 코드들을 합쳐서 작업하였습니다. 전체적인 흐름은 이전처럼 참고 페이지(www.lwh.jp/lisp/index.html)대로 따라갔고, 파이썬과 맞지 않는 부분들을 조금씩 수정하는 방식으로 진행했습니다. 여전히 리턴 때마다 에러와 결과값을 함께 튜플로 리턴해주는 방식을 사용하고 있지만 튜플을 unpacking하는 부분이 제대로 되어있지 않거나, 에러 타입이 깔끔하게 정리되지 않고 문자열로 전달해버리는 부분들이 많아 정리가 필요합니다.

from boilerplate import *
# from environment_mark2 import *

class Bindings:
    def __init__(self, parent):
        self.parent = parent
        self.symbols = dict()

    def add_symbol(self, symbol, value):
        if symbol.type == Type.SYM:
            self.symbols[symbol.value.upper()] = value

    def __str__(self):
        if isNil(self.parent):
            return f"ENV class : root"
        else:
            return f"ENV class : {str(self.parent)}"

    def __repr__(self):
        return self.__str__()

def env_create(parent):
    new_bindings = Bindings(parent)
    # if not isNil(parent):
    #     parent.add_child(new_bindings)
    return new_bindings

def env_get(env, symbol):
    parent = env.parent

    if symbol.value.upper() in env.symbols:
        return env.symbols[symbol.value.upper()]

    if type(parent) is Bindings:
        pass
    elif isNil(parent):
        return "Error unbound", nilp()

    return env_get(parent, symbol)

def env_set(env, symbol, value):
    env.add_symbol(symbol, value)
    return ErrorType.ERROR_OK

def listp(expr):
    while not isNil(expr):
        if expr.type != Type.PAIR:
            return False
        expr = expr.cdr()
    return True

def eval_expr(expr, env):
    if expr.type == Type.SYM:
        return ErrorType.ERROR_OK, env_get(env, expr)
    elif expr.type != Type.PAIR:
        return ErrorType.ERROR_OK, expr

    if not listp(expr):
        return ErrorType.ERROR_SYNTAX, nilp()

    op = expr.car()
    args = expr.cdr()

    if op.type == Type.SYM:
        if op.value.upper() == "QUOTE":
            if isNil(args) or not isNil(args.cdr()):
                return "Error_Args", nilp()
            return "Error_OK", args.car()
        elif op.value.upper() == "DEF":
            if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
                return "Error_Args", nilp()
            sym = args.car()
            if sym.type != Type.SYM:
                return "Error_Type", nilp()
            err, val = eval_expr(args.cdr().car(), env)

            env_set(env, sym, val)
            return "Error_OK", sym
        elif op.value.upper() == "LAM": # lambda
            if isNil(args) or isNil(args.cdr()):
                return "Error Args", nilp()

            return make_closure(env, args.car(), args.cdr())
    err, op = eval_expr(op, env)
    # if err:
    #     return err
    args = copy_list(args)
    p = args
    while not isNil(p):
        err, p.value[0] = eval_expr(p.car(), env)
        # if err:
        #     return err
        p = p.cdr()
    return apply(op, args)

    return "Error_Syntax", nilp()

def make_builtin(fn):
    a = Data()
    a.type = Type.BUILTIN
    a.value = fn
    return a

def make_closure(env, args, body):
    if not listp(args) or not listp(body):
        return "Error Syntax", nilp()

    p = args
    while not isNil(p):
        if p.car().type != Type.SYM:
            return "Error Type", nilp()
        p = p.cdr()

    result = cons(env, cons(args, body))
    result.type = Type.CLOSURE

    return "Error OK", result

def copy_list(lst):
    if isNil(lst):
        return nilp()

    a = cons(lst.car(), nilp())
    p = a
    lst = lst.cdr()

    while not isNil(lst):
        p.value[1] = cons(lst.car(), nilp())
        p = p.cdr()
        lst = lst.cdr()

    return a

def apply(fn, args):
    if fn.type == Type.BUILTIN:
        return "Error OK", fn.value(args)
    elif fn.type != Type.CLOSURE:
        return "Error Type", nilp()

    env = env_create(fn.car())
    arg_names = fn.cdr().car()
    body = fn.cdr().cdr()

    while not isNil(arg_names):
        if isNil(args):
            return "Error Args", nilp()
        env_set(env, arg_names.car(), args.car())
        arg_names = arg_names.cdr()
        args = args.cdr()

    if not isNil(args):
        return "Error Args", nilp()

    while not isNil(body):
        err, result = eval_expr(body.car(), env)
        # if err:
        #     return err
        body = body.cdr()

    return "Error OK", result

def builtin_car(args):
    return "Error OK", args.car()
    # if isNil(args) or not isNil(args.cdr()):
    #     return "Error Args", nilp()
    #
    # if isNil(args.car()):
    #     return "Error OK", nilp()
    # elif args.car().type != Type.PAIR:
    #     return "Error Type", nilp()
    # else:
    #     return "Error OK", args.car().car()

def builtin_cdr(args):
    return "Error OK", args.cdr()
    # if isNil(args) or not isNil(args.cdr):
    #     return "Error Args", nilp()
    #
    # if isNil(args.car()):
    #     return "Error OK", nilp()
    # elif args.car().type != Type.PAIR:
    #     return "Error Type", nilp()
    # else:
    #     return "Error OK", args.car().cdr()

def builtin_cons(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    return "Error OK", cons(args.car(), args.cdr())

def builtin_add(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    a = args.car()
    b = args.cdr().car()
    if a.type != Type.INT or b.type != Type.INT:
        return "Error Type", nilp()

    return "Error OK", mkint(args.car().value + args.cdr().car().value)

def builtin_subtract(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    a = args.car()
    b = args.cdr().car()
    if a.type != Type.INT or b.type != Type.INT:
        return "Error Type", nilp()

    return "Error OK", mkint(args.car().value - args.cdr().car().value)

def builtin_multiply(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    a = args.car()
    b = args.cdr().car()
    if a.type != Type.INT or b.type != Type.INT:
        return "Error Type", nilp()

    return "Error OK", mkint(args.car().value * args.cdr().car().value)

def builtin_divide(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    a = args.car()
    b = args.cdr().car()
    if a.type != Type.INT or b.type != Type.INT:
        return "Error Type", nilp()

    return "Error OK", mkint(args.car().value // args.cdr().car().value)

if __name__ == "__main__":
    env = env_create(nilp())

    env_set(env, mksym("CAR"), make_builtin(builtin_car))
    env_set(env, mksym("CDR"), make_builtin(builtin_cdr))
    env_set(env, mksym("CONS"), make_builtin(builtin_cons))
    env_set(env, mksym("+"), make_builtin(builtin_add))
    env_set(env, mksym("-"), make_builtin(builtin_subtract))
    env_set(env, mksym("*"), make_builtin(builtin_multiply))
    env_set(env, mksym("/"), make_builtin(builtin_divide))

    while True:
        parsedlist = Parser(Lexer(Input()._input()).lex())
        # print("\n===== === PAR === =====")
        # print(parsedlist)
        # print("===== === OUT === =====")
        print(eval_expr(parsedlist, env))
        # print(env)

 

Test

사칙연산

>> (define x (* 6 9))
('Error_OK', x)
>> x
(<ErrorType.ERROR_OK: 0>, ('Error OK', 54))
>> (- x 12)
Traceback (most recent call last):

→ 튜플을 제대로 unpacking 하지않고 리턴해 받다보니 x가 Data 타입이 아니라 튜플이 되어 오류가 발생하는 것 같습니다.

 

Lambda expression and closures

>> (define square (lambda (x) (* x x)))
('Error_OK', square)
>> (square 3)
('Error OK', ('Error OK', 9))
>> (square 4)
('Error OK', ('Error OK', 16))
>> ((lambda (x) (- x 2)) 7)
('Error OK', ('Error OK', 5))
>> (define make-adder (lambda (x) (lambda (y) (+ x y))))
('Error_Args', NIL)

→ 어디선가 오류가 발생해 제대로 동작하지 않는데, 디버거를 통해 잘 찾아 조금만 수정하면 고칠 수 있을것 같습니다.

'kos' 카테고리의 다른 글

Lab 4.1a[김예령]: 조건, 비교  (0) 2021.04.29
Lab 3.1a[김예령]: 람다와 클로저  (0) 2021.04.29
Lab 2.2b[김예령]: 기본 함수  (0) 2021.04.27
Lab 3.1: 람다와 클로저  (0) 2021.04.27
Lab 2.1a[박인철]: 환경  (0) 2021.04.27
Comments