这个tree calculus和lambda calculus有什么区别?我看了下,个人感觉似用 ()
和对事物(不知道该用什么词好)进行编码,而lambda是function?
read the paper
又到了历史学魅力时刻。在CLtL(《Common Lisp the Language》,ANSI之前的Common Lisp社区标准)和CLtL2中,list是可以被视作function类型的。原文摘录:
A lambda-expression (a list whose car is the symbol lambda) may serve as a function. Depending on the implementation, it may be possible for other lists to serve as functions.
这个是从MacLisp等前辈中来的,它们说的就很模糊,显而易见地带来了诸多不便。于是1988年X3J13委员会就有了这么个提案:
allowing symbols but not lists to be FUNCALLed or APPLYed
… In particular, a list may not be used to implement any FUNCTION subtype.
然后list和function就这么被区分开了。不过Emacs的出现早于X3J13,加上elisp并不是很需要区分这俩,所以这个更改就没同步到elisp上面来
请教下,这两句话是指:
((lambda (a) a) 2) ;;=> 2
而
((car '((lambda (a) a))) 2) ;; => error
吗?
还是说,不推荐下述写法:
(funcall (list 'lambda ...) ...)
这句说的模糊是指 may server as a function,and/or Depending on the implementation? 光从它对lambda表达式的定义看,我(很不可靠的)感觉很清晰。所以不知道,你说的模糊指?
ELISP> (funcall (cons 'function '((lambda (x y) (sqrt (* x y))))) 2 2)
*** Eval error *** Invalid function: #'(lambda (x y) (sqrt (* x y)))
ELISP> (funcall #'(lambda (x y) (sqrt (* x y))) 2 2)
2.0
ELISP> (funcall (list (quote lambda) (list (quote x) (quote y)) (list (quote +) (quote x) (quote y)))
1 2)
3
a list whose car is the symbol lambda
建议先看懂这句话
ELISP> (car (cons 'function '((lambda (x y) (sqrt (* x y))))))
function
怎么理解?
ELISP> (car `(,(make-symbol "lambda") nil t))
lambda
ELISP> (symbolp (car `(,(make-symbol "lambda") nil t)))
t
ELISP>
ELISP> (eq (make-symbol "lambda") 'lambda)
nil
打印出来一样不代表相同
我说说,我这么写的原因,麻烦您看下我哪里存在问题。
#'xxx 等价于 (function xxx)
(cons 'function '((lambda (x y) (sqrt (* x y)))) =>
(function (lambda (x y) (sqrt (* x y))) =>
#'(lambda (x y) (sqrt (* x y)))
然后,我对 quote
的理解,和 function
一样,即:
(quote foo)
等于 'foo
, (function foo)
等于 #'foo
。这理解是否存在错误?
ELISP> #'(lambda (x y) (sqrt (* x y)))
(closure
(t)
(x y)
(sqrt
(* x y)))
ELISP> (cons 'function '((lambda (x y) (sqrt (* x y)))))
#'(lambda
(x y)
(sqrt
(* x y)))
请问哪里一样了?
这样能看懂了吧?
ELISP> #'x
x
ELISP> ''x
'x
ELISP> (cons 'quote '(x))
'x
懂了。cons那句返回了个list,打印成#'…;function那句返回了个closure对象。
也就是说,某种程度上(不用make-symbol),拼接 symbol 是可以的,需将拼接出的 list 再 eval 一次。
而上文的理解错在
(cons 'function '((lambda (x y) (sqrt (* x y)))) =X=> ;;
(function (lambda (x y) (sqrt (* x y)))
实际是
(cons 'function '((lambda (x y) (sqrt (* x y)))) ===>
'(function (lambda (x y) (sqrt (* x y)))
也就是說,你重新发现了 macro
绝大多数情况下 (funcall (list 'lambda ...) ...)
是非法的——实际上并不是“写法”的问题,而是你举的这两个例子里面的表达式就是两种完全不同的对象:你把lambda表达式quote起来,或者用list表达式拼装起来,它就是未经过求值的列表;而没有被quote的lambda表达式会被求值,就像普通的表达式一样。而lambda表达式是怎么求值的呢?在CL(包括Elisp)里,lambda
其实是一个宏:
Provides a shorthand notation for a function special form involving a lambda expression such that:
(lambda lambda-list [[declaration* | documentation ]] form*) == (function (lambda lambda-list [[declaration* | documentation ]] form*)) == #'(lambda lambda-list [[declaration* | documentation ]] form*)
所以其实经过求值后的(lambda () ...)
和#'(lambda () ...)
是一样的,我一直是省着写2333
回到(funcall (list 'lambda ...) ...)
,鉴于目前绝大多数Common Lisp实现的*features*
里都没有:cltl1
和:cltl2
,这个行为理论上到哪都是非法的。实际中,这个表达式在SBCL和LispWorks里会报出“你call的不是一个函数”的错。但在ECL里它是可以正常执行的。这就属于implementation-dependent了。
这个已经很模糊了x。首先“Depending on the implementation”就说明它是一个未经规范的行为,作为一种基本数据类型、“first-class citizen”,function的语法都未经规范,这个想必相当糟糕(x
其次,X3J13的issue里也提到了,由于这个模糊的定义,typep
这个基本的类型检查函数就无法准确地检查function,连带着typecase等语句的结果也不可靠。因为一个list它是不是function完全由funcall
和apply
这俩函数决定,那我们总不能用“运行一下看看”来做类型检查吧,这个空间可太模糊了orz
嗯,感谢解答,有些细节我还得再想想。
((lambda (a) a) t) ; => t。 (*)
(*)让我有种一些错觉:
1)这个表达式“看”上去如同一个list,其car也是一个list。在这个层面上,它的执行结果符合我对lambda calculus的“预期”。也如同其他(foo 'bar)
一样(此处foo是一个function or whatever)。
现在起,现实里开始有了function的概念(此function非数学中的function,而是某种lisp object,且此object(我目前理解)是从面向对象里边借用的概念),最初的lambda calculus开始因为实现问题分化出lisp-1、lisp-2版本。
funcall
是新的认知代价。
2)我开始误认为:好吧,只要list的car的东西是function,那它就可以求值、执行、eval、funcall。于是,我开始疑惑什么是function?
对我而言,(*)至少从表现上是一种function,或者说(lambda …)是一种function。此刻的我还没有“宏”的概念,不期望去关心lambda究竟如何实现。因为关心lambda是什么似乎很怪,就像在问lambda calculus里的λ究竟是什么?
而这个lambda是“宏”的实现细节似又一次fool了我:
因为它是宏,也许恰巧eval得保持自身。所以某种程度上,它的print和read输出一致。然而,如今lambda eval后开始成为closure。说实话,我不是很想深究实现细节。因为只要这样做,很快我就会发现,lambda至少有三种:‘(lambda …),(closure …),#’(lambda …)。这对于最初只认λ演算的我来说有点懵。进而导致(#'foo 1)
这种显然是“语法”错误的问题。
尽管我很难评价我究竟该听谁的“语法”。
从λ演算纯粹的list与symbol,到function等各种各样的type,又到object,print与read之分。
我开始回想:我试写lisp的初衷是因它给了我一种(也许又是错觉的)感觉——语法可以自定义、可以及其灵活地扩展。可当我拿起它的时候,好像语法规则的担子变得更加沉重且变幻莫测了。
甚至CLOS?又是一堆types, objects.
I just want to write my own type with lambda list and symbol!(抱歉,有点糟心。)
此刻,看到Y combinator,我再次认知到,要将公式译成各家lisp,还得过各家的“语法”关。
我都有λ calculus了,为什么不自己定义语法呢?
再次抱歉,绝无恶意。
再次抱歉。
只是提供一个外行人的使用体验、感觉:原本想摆脱“语法”却成了更重的负担。
lisp对表达式递归地求值,直到遇到quote,或者字面量。相当于给定一棵树,lisp只会按固定顺序(比如从左到右)做dfs。
按照lisp的调用约定,对一个list求值,它的第一个元素,即rfold形状的二叉树的最左子树,是一个lisp原语,或者可以进行lambda绑定的表达式。可以进行lambda绑定,并不代表它必须通过lambda这个符号去定义。
如果你想改变lisp的求值顺序,就需要引入宏,因为宏被设计为不对它的参数求值的函数。这样就可以通过扭一扭这个树改变求值顺序。这是lambda calculus之外的东西,宏需要lisp解释器实现额外的支持。
所以之前提到tree calculus那个paper,因为它很直观,而且不需要在解析器层面区分宏和函数,它的组合子逻辑包含了向下和歪扭的操作。我学尚浅,讲的不如原paper。
过谦了。
关于tree calculus,这两个comment让我对它有初步的认知(贴这留人)。
下述博文用图像更直观地解释了tree calculus的apply rules,且展示一种应用:基于类似λ的东西(符号Δ),定义出上层boolean、number、combinatory logic:
然而,我不明白它能做的相比于下述用lisp做的有何种实质上的区别:
怎么理解,进一步说:它可以通过额外引入的组合子解决求值顺序问题吗?
理解。只要试过写lisp解释器,总会碰上这件事情。(这些事情有意思的地方在于为什么设计时要这样抉择。)顺便贴个同样涉及求值顺序的博文,可以对比里头用scheme实现的Y combinator与用elisp实现的,与公式上的,看看究竟额外引入了哪些“概念”、“实体”。 https://czlwang.com/zettel/20201007005002-y_combinator.html
嗯,理解了。是我把 λ-calculus 里 apply 时可能会用到的括号 和 elisp 里 list 的括号混淆了。
我不负责任地拍脑袋想:因为 ((lambda (a) a) 2)
等于 (funcall (lambda (a) a) 2)
,而最初的 lambda-expression
只是一个 lambda
符号位于 car 的 list 。为了规避求值错误,出现了模糊的写法 (funcall '(lambda (a) a) 2)
;后来,lambda
改宏,于是,我们又可以“直观”地用 (funcall (lambda (a) a) 2)
了;再后来,(我不知道)为什么改推荐全改 “#'(lambda ...)
” 呢?我不知道是在 elisp manual 里还是哪里看过这样的推荐描述。
不存在这种推荐,少瞎想
建议先读 Autonomy of Lisp,了解一下啥叫编程语言的语义,不要再发拍脑袋的意见了。
编程语言表达式求值的结果是确定的,不会因为你瞎几把乱想改变
我只能说你想象力是够丰富了,但不去认真求证就乱下结论,过于有毒了。所谓思而不学。
笑。说实话,我也不知道忘了哪里看来的,但如果后续碰到,我会回来贴这帖子里。总之,这各家的概念和 implement details 让我深感懵逼。当然,这全怪我懒得读标准。