使用堆栈而不是递归来实现Lisp eval

时间:2015-06-25 10:25:34

标签: c++ recursion stack lisp

我正在查看howtowriteaprogram.blogspot.com/2010/11/lisp-interpreter-in-90-lines-of-c.html,并希望看到如何在没有递归的情况下实现eval()函数,而是使用堆栈作为迭代函数。

以下是文章中的eval()代码:

////////////////////// eval

cell eval(cell x, environment * env)
{
    if (x.type == Symbol)
        return env->find(x.val)[x.val];
    if (x.type == Number)
        return x;
    if (x.list.empty())
        return nil;
    if (x.list[0].type == Symbol) {
        if (x.list[0].val == "quote")       // (quote exp)
            return x.list[1];
        if (x.list[0].val == "if")          // (if test conseq [alt])
            return eval(eval(x.list[1], env).val == "#f" ? (x.list.size() < 4 ? nil : x.list[3]) : x.list[2], env);
        if (x.list[0].val == "set!")        // (set! var exp)
            return env->find(x.list[1].val)[x.list[1].val] = eval(x.list[2], env);
        if (x.list[0].val == "define")      // (define var exp)
            return (*env)[x.list[1].val] = eval(x.list[2], env);
        if (x.list[0].val == "lambda") {    // (lambda (var*) exp)
            x.type = Lambda;
            // keep a reference to the environment that exists now (when the
            // lambda is being defined) because that's the outer environment
            // we'll need to use when the lambda is executed
            x.env = env;
            return x;
        }
        if (x.list[0].val == "begin") {     // (begin exp*)
            for (size_t i = 1; i < x.list.size() - 1; ++i)
                eval(x.list[i], env);
            return eval(x.list[x.list.size() - 1], env);
        }
    }
                                            // (proc exp*)
    cell proc(eval(x.list[0], env));
    cells exps;
    for (cell::iter exp = x.list.begin() + 1; exp != x.list.end(); ++exp)
        exps.push_back(eval(*exp, env));
    if (proc.type == Lambda) {
        // Create an environment for the execution of this lambda function
        // where the outer environment is the one that existed* at the time
        // the lambda was defined and the new inner associations are the
        // parameter names with the given arguments.
        // *Although the environmet existed at the time the lambda was defined
        // it wasn't necessarily complete - it may have subsequently had
        // more symbols defined in that environment.
        return eval(/*body*/proc.list[2], new environment(/*parms*/proc.list[1].list, /*args*/exps, proc.env));
    }
    else if (proc.type == Proc)
        return proc.proc(exps);

    std::cout << "not a function\n";
    exit(1);
}

我被绊倒的第一个地方是想知道如何用堆栈实现“if”逻辑。如果你将条件(第一个单元格)放在堆栈上并进入下一次迭代,你怎么知道回到那时你决定是否分支到“then”或“else”单元/节点? / p>

同样适用于任何其他逻辑:例如,如果我将“define”的单元格放在堆栈上并转到下一次迭代,一旦评估完毕,我怎么知道要回来到同一个地方?

1 个答案:

答案 0 :(得分:2)

我已经完成了两次。一次in Common Lispin Brainfuck一次。 基本思想是将原语和应用程序或推送元素作为堆栈。每个部分都有一个目标,因此eval过程依赖于通过地址突变cons细胞。一个例子。 #是实际地址而不是符号:

(cons 'a 'b) 
#result      ->

cons 
#x
(#preapply #x 'a 'b)
#result              ->

(#preapply #cons 'a 'b) ->

'a
#a
'b
#b
(#apply #cons #a #b)
#result                 ->

quote
#r
(#preapply #r a)
#a
'b
#b
(#apply #cons #a #b)
#result                 ->

'b
#b
(#apply #cons #a #b)
#result                 ->

'b
#b
(#apply #cons a #b)
#result                 ->

quote
#r
(#preapply #r b)
#b
(#apply #cons a #b)
#result                 ->

(#preapply #quote b)
#b
(#apply #cons a #b)
#result                 ->

(#apply #cons a b) 
#result                 ->

<empty stack>

结果,(a.b)在地址#result的车辆中找到。

#preapply是一个处理特殊表单的基元,例如iflambdaflambda。它还推动了结构,以便在每个参数时对其进行评估。一个功能。 #apply对于lambdas的原语和堆栈以及环境管理非常简单。

在我的实现中,特殊形式和基本函数具有最低地址,因此基元只是内存中的特殊位置。