我正在试图弄清楚如何实现Lisp评估 的非递归即可。我的基于C的评估者是Minimal Lisp文件l1.c.然而 那里的几个函数再次进入eval:eval,apply, evargs,evlist以及Lisp Ops defineFunc,whileFunc,setqFunc,ifFunc ...
我正在尝试找出持平的评估。 我可以提出一些可行的方法:
答案 0 :(得分:4)
Lisp的一个非常重要的方面,实际上是许多函数式语言的一个重要方面,它是 compositional 。这意味着表达式的含义是使用其子表达式的含义来定义的 - 换句话说,评估的定义本质上是递归的。在非函数式语言中,表达式与语句之间存在一些差异,但即使表达式也不以某种方式限制,因此递归也被引入定义。可能只是语言定义的递归性不那么明显的唯一情况是汇编语言。 (尽管有意义的定义当然需要归纳。)
因此,对eval
的一些递归定义的争论是你将失去的。如果你使用编译到机器代码,那么代码将是递归的(并且生成代码也将是递归的)。如果您通过Forth评估程序进行评估,那么该评估程序仍将是递归的。即使你在另一个答案中使用了CPS建议,你最终还是会有另一个堆栈编码。
所以最重要的是你可以得到的最好的是一些不直接使用机器堆栈的堆栈编码 - 没有实质性区别,但你通常会失去性能(因为CPU处理 < / em>非常有效地堆栈,并且它在堆上的编码会变得更慢。)
答案 1 :(得分:3)
请参阅此主题:Continuation Passing Style
答案 2 :(得分:2)
在C中实现Lisp-evaluationator时,C编译器使用堆栈 生成子程序调用的控制流程。实现无堆栈 C语言中的求值器需要使用goto和。手动编写控制流 开关():
v *
evargs(ctx *cctx, v *l, v *env)
{
struct v *r = 0;
if (l) {
r = eval(cctx, car(l),env);
r = mkCons(r,evargs(cctx, cdr(l),env));
}
return r;
}
得
case EVARGS_0:
S_SET(0,0); /* EVARGS_0: r = 0; */
if (!(v=S(2))) /* if (l) */
goto ret;
RCALL_EVAL(EVARGS_1, car(v)); /* r = < eval(cctx, car(l),env); > */
break;
case EVARGS_1:
S_SET(3,S(1)); /* EVARGS_1: < r = ... > */
RCALL_EVARGS(EVARGS_2, cdr(S(2))); /* r = mkCons(r, < evargs(cctx, cdr(l),env) > ); */
break;
case EVARGS_2:
S_SET(0,mkCons(S(3),S(1))); /* EVARGS_2: < r = mkCons(r, evargs(cctx, cdr(l),env) ); > */
goto ret;
答案 3 :(得分:0)
我认为您可能缺少的关键洞察力是在lisp解释器中,最小的一组函数被实现为非递归的基元。确切的原语集有所不同,但包括cons,car,cdr和apply的“最原始”版本,它执行这些实际函数之一而不是自身的解释版本。
你应该查阅John McCarthy的原始论文和/或John Allen的Lisp 1.5
答案 4 :(得分:0)
我记得有一本关于HOPE编程语言的书。它是一种类似于ML的功能语言。它对编译器进行了全面的概念性描述,并对函数式语言编译器提出了一般性的看法。一个观察结果是关于Y-combinator的争论。作者提出,处理递归函数的一种可能方法是Y-combinator的实现,并将每个递归函数转换为可以使用Y-combinator重现的非递归函数。
这将是if
特殊形式的类似技巧,它为语言中可能需要的各种惰性评估提供(通常也足够)。以类似的方式,您可以限制所有函数的递归,但引入了一个特殊的Y
函数,允许递归开始。