相关代码是eval
&的第一版。 apply
看起来像现代lisp(我知道)。
因为正确答案可能已知但丢失了(我的google-fu很不错,至少我搜索了20分钟) 我将奖励第一个正确的答案(我将查看编辑时间,所以不要试图作弊)250点奖励尽快。
我会建议其他人也为赏金做出贡献, 请记住上面的视频Alan Kay说这些东西让人想起Einstein发现Theory of Relativity时所处的环境。
文本中的代码用M-Expressions编写。我正在研究翻译器从M表达式转换为S表达式(lisp代码)@ https://github.com/Viruliant/MccarthyMCEval-1.5。
无论如何,这里是第13页的翻译引用:
;______________________________________Lisp Meta-Circular Evaluator S-Expression
;this code is written in the order it appears on pages 10-13 in the Lisp 1.5 Manual
;and is translated from the m-expressions into s-expressions
(label mc.equal (lambda (x y)
(mc.cond
((atom x) ((mc.cond ((atom y) (eq x y)) ((quote t) (quote f)))))
((equal (car x)(car y)) (equal (cdr x) (cdr y)))
((quote t) (quote f)))))
(label mc.subst (lambda (x y z)
(mc.cond
((equal y z) (x))
((atom z) (z))
((quote t) (cons (subst x y (car z))(subst x y (cdr z)))))))
(label mc.append (lambda (x y)
(mc.cond
((null x) (y))
((quote t) (cons (car x)) (append (cdr x) y)))))
(label mc.member (lambda (x y)
(mc.cond ((null y) (quote f))
((equal x (car y)) (quote t))
((quote t) (member x (cdr y))))))
(label mc.pairlis (lambda (x y a)
(mc.cond ((null x) (a)) ((quote t) (cons (cons (car x)(car y))
(pairlis (cdr x)(cdr y) a)))))
(label mc.assoc (lambda (x a)
(mc.cond ((equal (caar a) x) (car a)) ((quote t) (assoc x (cdr a))))))
(label mc.sub2 (lambda (a z)
(mc.cond ((null a) (z)) (((eq (caar a) z)) (cdar a)) ((quote t) (
sub2 (cdr a) z)))))
(label mc.sublis (lambda (a y)
(mc.cond ((atom y) (sub2 a y)) ((quote t) (cons (sublis a (car y))))
(sublis a (cdr y)))))
(label mc.evalquote (lambda (fn x)
(apply fn x nil)))
(label mc.apply (lambda (fn x a)
(mc.cond ((atom fn) (
(mc.cond
((eq fn car) (caar x))
((eq fn cdr) (cdar x))
((eq fn cons) (cons (car x)(cadr x)))
((eq fn atom) (atom (car x)))
((eq fn eq) (eq (car x)(cadr x)))
((quote t) (apply (eval (fn a)x a))))))
((eq (car fn) lambda) (eval (caddr fn) (parlis (cadr fn) x a)))
((eq (car fn) label) (apply (caddr (fn)x cons (cons (cadr (fn)))
(caddr fn))a)))))
(label mc.eval (lambda (e a)
(mc.cond
((atom e) (cdr (assoc e a)))
((atom (car e)) (mc.cond
((eq (car e) quote) (cadr e))
((eq (car e) cond) (evcon (cdr e) a))
((quote t) (apply (car e) (evlis (cdr e) a) a))))
((quote t) (apply (car e) (evlis (cdr e) a) a))))))
(label mc.evcon (lambda (c a)
(mc.cond
((eval (caar c) a) (eval (cadar c) a))
((quote t) (evcon (cdr c) a)))))
(label mc.evlis (lambda (m a)
(mc.cond
((null m) (nil))
((quote t) (cons (eval (car m) a) (evlis (cdr m) a)))))))
答案 0 :(得分:13)
有两个不同的问题:
首先:动态绑定为错误
不确定他的意思,但通常在McCarthy的EVAL
中dynamic binding
的使用可以被视为 bug 。他没有为变量实现词法范围。例如, bug 出现在这里:
http://www-formal.stanford.edu/jmc/recursive/node3.html
查看函数maplist
和diff
。两者都使用x
。由于早期的Lisp提供动态绑定,因此无法正常工作。
一个更简单的示例,显示评估者使用动态绑定
请注意使用eval.
,即McCarthy的eval
。
CL-USER 36 > (eval. '((lambda (f)
((lambda (x) (f))
'foo))
'(lambda () x))
nil)
这将返回FOO
,因为从动态绑定中查找X
的值。
如果我们查看代码,则需要我们将函数作为列表传递:'(lambda () x))
。这将评估为一个列表。稍后,列表将通过(f)
调用 - 没有参数。然后该列表被解释为 lambda表达式,并且x
将通过查看x
的当前动态绑定来解析。 x
引入了FOO
与((lambda (x) (f)) 'foo)
的绑定。这将被使用。
在60年代的Lisp 1.5实现中,人们可能会写一些类似于:
的内容((lambda (f)
((lambda (x) (f))
'foo))
(function (lambda () x)))
请注意(function (lambda () x))
评估标记,动态环境和函数的列表。不幸的是,Lisp 1.5实现仍然使用动态绑定。所以这已经是正确的方向,但 bug 并没有真正解决。改进的是当一个人将函数作为参数传递给其他函数时的情况。
FUNARG问题
在60年代/ 70年代初花了很长时间才弄明白这个问题。它被称为 FUNARG问题。例如,见Joel Moses论文:The Function of FUNCTION in LISP, or Why the FUNARG Problem Should be Called the Environment Problem。有各种解决方案来创建闭包并使用词法绑定。解释器通常使用动态绑定,编译器使用词法绑定。在Lisp世界中,这基本上是在 Scheme 中解决的,它默认引入了词法绑定。然后,这个词法绑定允许使用闭包来模拟对象系统(Kay可能认为有用的东西)。参见文章:1975年的SCHEME: An Interpreter for Extended Lambda Calculus。
在Common Lisp中,默认情况下使用词法范围,如Lisp方言Scheme,上面的示例将是一个错误(这里我们使用Common Lisp中的eval
,稍微更改了代码使其成为合法的Common Lisp代码):
CL-USER 43 > (eval '((lambda (f)
((lambda (x) (funcall f))
'foo))
(function (lambda () x))))
Error: The variable X is unbound.
正如您在Common Lisp(和Scheme)中看到的那样,(lambda () x)
是一个真正的 lambda表达式,而不是引用列表和(function (lambda () x))
求值为函数对象 - 如果有 bindings ,则它是闭包 - 一个函数对象及其绑定。传递此函数object / clojure,然后通过(funcall f)
调用。由于x
没有引用(它是自由变量)并且没有通过绑定查找,因此执行代码时会发生错误。这就是我们想要的:我们需要词法绑定,而我们的代码中的这个错误就是结果。麦卡锡的原始Lisp中没有发生这个错误是 bug 的一个结果。修复这个错误(花了十多年才完全满意),使我们能够在Lisp中使用闭包 - 就像在Common Lisp中一样,它从Scheme中学习它。
可能Kay还将动态绑定视为 bug 。这是一个非常基本的问题,理解/解决它,有助于设计和开发更好的编程语言。
请注意,典型的早期Smalltalk实现(例如Xerox' Smalltalk 80)也使用了动态绑定。
麦卡锡关于 bug
在From LISP 1 to LISP 1.5(1979)麦卡锡写道(粗体由我):
d。 自由变量。在完全无辜的情况下,James R. Slagle编写了以下LISP函数定义,并在它没有正常工作时抱怨:
该函数的目的是找到满足p [x]并返回f [x]的x的子表达式。如果搜索不成功,则计算无参数的连续函数u []并返回其值。困难在于当发生内部递归时,car [x]所需的值是外部值,但实际使用了内部值。在现代术语中,需要词法范围,并获得动态范围。
我必须承认,我认为这个难度仅仅是一个 bug ,并表示相信Steve Russell会很快解决它。他确实解决了这个问题,但他发明了所谓的FUNARG设备,该设备将词汇环境与功能性参数结合在一起。类似的困难后来出现在Algol 60中,而Russell则成为解决问题的更全面解决方案之一。虽然它在解释器中运行良好,但在编译代码中似乎反对全面性和速度,这导致了一系列的妥协。不幸的是,时间不允许写一个附录给出问题的历史,感兴趣的读者被称为(摩西1970)作为一个开始的地方。 (David Park告诉我,Patrick Fischer也参与了FUNARG设备的开发)。
这与第二个问题无关:
第二:同一本书中不同版本的EVAL中的错误
请参阅Paul Graham的The Roots of Lisp,讨论本书后面的EVAL定义中的错误。在第12页上,您可以找到McCarthy代码中的错误描述,该错误会导致对命名函数的参数进行双重评估。
答案 1 :(得分:3)
很可能是对动态范围错误的引用(自那以后的一个bug) 结果并没有做它应该做的事情,如果它预期是 类似于lambda演算):
hello()
在编辑过的半流派类文本中:
eq[car[fn];LAMBDA] -> eval[caddr[fn];pairlis[cadr[fn];x;a]];
%^^^^^^^^^^^^^^^^^^^^^
但这根本没有用。修复必须不只是那里,in 那个地方;你需要了解整个事情的运作方式 真的关注它,看看bug是如何发生的。
作为正确方向的快速提示,这里的问题是
lambda函数体在一个环境中进行评估(第二个
((eq (car fn) lambda) (eval (caddr fn) (parlis (cadr fn) x a)))
;^^^^^^^^^^^^^^^^^^^^^^
)的参数,它是当前环境的扩展,
产生动态范围。解决它 - 实现词汇范围
---涉及的不仅仅是编辑,因为有关的信息
函数的词法范围已经丢失。
(关于PLs的任何随机正确的书都应该有更多的细节 Lisp的上下文,深入挖掘确实会让你进入整个FUNARG 的东西。)
答案 2 :(得分:3)
看起来像
equal[x;y] =
[atom[x] -> [atom[y] -> eq[x;y]; T -> F];
equal[car[x];car[y]] -> equal[cdr[x];cdr[y]];
T -> F]
不处理x
是一个缺点且y
是原子的情况
编辑:如果找不到密钥,assoc
也将无法返回nil。
答案 3 :(得分:2)
update2:以下是本文中的代码,在所有 13 代码行中以一些伪代码重写,包含列表模式和理解(包括并行理解) em> 15 添加FUNARG
设备,如下所述),所有这些都在一个定义中:
apply f args = eval [f, ...[[QUOTE, a] | a <- args]] [] % McCarthy-LISP-paper
eval e a | atom e = head [v | [n, v] <- a, n == e]
| otherwise = case e of
[QUOTE, x] -> x
[FUNCTION, x] -> [FUNARG, x, a] % enclose the definitional environment!
[CONS, x, y] -> [eval x a, ...eval y a]
[CAR, x] -> head ( eval x a )
[CDR, x] -> tail ( eval x a )
[ATOM, x] -> atom ( eval x a )
[EQ, x, y] -> eval x a == eval y a
[COND, ...xs] -> head [eval c a | [t, c] <- xs, eval t a]
[[LAMBDA,p,b], ...xs] -> eval b [...[[n, eval x a] | n <- p | x <- xs], ...a]
[[LABEL, n,x], ...xs] -> eval [x, ...xs] [[n, [LABEL, n, x]], ...a]
[[FUNARG,f,d], ...xs] -> eval [f, ...[[QUOTE, eval x a] | x <- xs]] d
[x, ...xs] -> eval [eval x a, ...xs] a % fixing the bug,
%% eval [eval x a, ...[eval x a | x <- xs]] a % args eval'd twice
更新:这里是一个video by Philip Wadler,他在那里谈论(在22:30)关于“bug”,以及如何使用“错误的变量”(对于要用于的环境)在后面的伪代码中评估 lambda-body ,a
而不是env
会导致“动态绑定”而不是“静态绑定”。
我在书中的代码(Lisp 1.5程序员手册)中重新编写了原始的M表达式,使用:
{{1}来更容易让我遵循一些ad-hoc伪代码或者cons
,带有模式匹配等,很快就会出现。
实际上,我看不出任何双重评估问题。 .
期望已经评估了它的参数,apply
对它们进行了评估。
我认为这个错误出现在原始论文中"Recursive Functions of Symbolic Expressions"(更新:是的!Rainer Joswig在他的回答结尾处提到保罗格雷厄姆的文章,其中指出这是参考论文 中的代码)。
所以确实,Alan Kay的评论可能与动态范围有关。他提到“在第13页的底部”(所以,书中的 ),这就是eval
定义所在的位置,它定义了同样的列表元素,当前< / em>环境。查看本书第70,71页的解决方案,要求程序员显式使用新关键字evlis
标记其lambda定义,以创建function
- 标记列表在评估funarg
表单时将lambda与(或关闭,如在“闭包”中)环境打包在一起 - 即function
形式的定义环境。
Moses 1970将其称为绑定环境,在高阶函数调用中用作函数参数时仅讨论闭包的隐式创建(因此“funarg”绰号)。这也是更多当代关于“深度和浅层绑定”(在我看来,滥用术语,在Perl等中)的讨论的背景。
但是看一下本书中的扩展版本,似乎它允许程序员明确地创建 这样的实体,将它们存储在一个变量中,然后将它传递给任何其他 class 实体。然后,当应用闭包(lambda
- 标记列表)时,lambda定义在其环境中进行评估,而不是当前环境(Moses 1970称之为激活环境)。这非常接近于令人信服地模仿动态绑定的词法范围。
这是伪代码,遵循书中的代码:
funarg