对于Racket编程语言,为什么lambda不被视为函数?
例如,它不能被定义为这样的高阶函数。
(define (my-lambda args body)
(lambda args body))
答案 0 :(得分:2)
lambda
需要是Scheme中的核心语言功能(例如if
,let
,define
),因为它正在构建closure所以需要管理已关闭的集合或free variables(并以某种方式将其绑定放在闭包中)。
例如:
(define (translate d) (lambda (x) (+ d x)))
当您调用或评估(translate 3)
时,d
为3,因此动态构造的闭包应该记住d
绑定到3. BTW,您通常需要{{1}的结果}和(translate 3)
是两个不同的闭包共享一些常见的代码(但(translate 7)
具有不同的绑定)
另请阅读λ-calculus。
解释所有细节都需要整本书。幸运的是,C。Queinnec写过它,所以请阅读他的Lisp In Small Pieces书。
(如果你读法语,你可以阅读该书的最新法语version)
另请参阅Kernel programming language。
另请阅读关于evaluation strategy的wikipage。
PS。你可以和一些Lisp实现(特别是MELT,可能SBCL)这样做,将d
定义为macro -e.g。这将扩展为以特定于实现的方式构建一些闭包(但lambda
不能被定义为函数)。
答案 1 :(得分:2)
您的问题缺失了一个关键区别:
lambda
是语法。 lambda
表单是一种表达形式,其值是一个过程。是否" lambda是一个函数"从类型错误开始,可以这么说,因为lambda和程序不会生活在同一个世界。
但是,让我们把它放在一边。另一种看待这种情况的方法是根据评估规则来考虑它。对于参数的过程应用,默认的Scheme评估规则可以用伪代码表示,如下所示:
(define (eval-application expr env)
(let ((values
;; Evaluate each subexpression in the same environment as the
;; enclosing expression, and collect the result values.
(map (lambda (subexpr) (eval subexpr env))
expr)))
;; Apply the first value (which must be a procedure) to the
;; other ones in the results.
(apply (car values) (cdr values))))
英文:
apply
第一个结果(必须已经评估过程)到其余的列表中。现在,lambda
无法成为某个程序的另一个原因是此评估规则对lambda
表达式不起作用。特别是,lambda
的要点是不立即评估它的身体!特别是这会折磨你的my-lambda
- 如果你试图以这种方式使用它:
(my-lambda (x) (+ x x))
...必须立即将中间的(x)
评估为在整个表达式出现的环境中调用名为x
的过程。还必须立即评估(+ x x)
。
因此lambda
需要自己的评估规则。正如Basile的回答所指出的那样,这通常是作为Scheme系统实现中的原语实现的,但是我们可以用伪代码将其绘制成类似的东西:
;;;
;;; Evaluate an expression of this form, returning a procedure:
;;;
;;; (lambda <formals> <body> ...)
;;;
(define (eval-lambda expr env)
(let ((formals (second expr))
(body (cddr expr)))
;; We don't evaluate `body` right away, we return a procedure.
(lambda args
;; `formals` is never evaluated, since it's not really an
;; expression on its own, but rather a subpart that cannot
;; be severed from its enclosing `lambda`. Or if we want to
;; say it all fancy, the `formals` is *syncategorematic*...
(let ((bindings (make-bindings formals args)))
;; When the procedure we return is called, *then* we evaluate
;; the `body`--but in an extended environment that binds its
;; formal parameters to the arguments supplied in that call.
(eval `(begin ,@body) (extend-environment env bindings))))))
;;;
;;; "Tie" each formal parameter of the procedure to the corresponding
;;; argument values supplied in a given call. Returns the bindings
;;; as an association list.
;;;
(define (make-bindings formals args)
(cond ((symbol? formals)
`((,formals . args)))
((pair? formals)
`((,(car formals) . ,(car args))
,@(make-bindings (cdr formals) (cdr args))))))
要理解这个伪代码,经过时间考验的是研究如何构建元循环解释器的许多Scheme书籍之一(用Scheme编写的Scheme解释器)。请参阅示例this section of Structure and Interpretation of Computer programs。
答案 2 :(得分:2)
函数调用(e0 e1 e2)的评估方式如下
f
的函数体在其中的环境中进行评估
形式参数绑定到值v1
和v2
。
请注意,在激活函数体之前,将评估所有表达式e0,e1和e2。这意味着像(foo #t 2(/ 3 0))这样的函数调用会在评估(/ 3 0)时导致错误 - 在将控制权移交给foo的主体之前。
现在考虑特殊形式lambda
。在(lambda (x) (+ x 1))
中,这会创建一个变量x
的函数,当使用值v
调用时,它将计算(+ v 1)
。
如果相反lambda
是一个函数,则在(x)
的主体被激活之前评估表达式(+ x 1)
和lambda
。现在(x)
很可能会产生错误 - 因为(x)
意味着调用没有参数的函数x
。
简而言之:在将控件传递给函数体之前,函数调用将始终评估所有参数。如果不评估某些表达式,则需要特殊形式。
此处lambda
是一个表单,不会评估所有子表单 - 因此lambda
需要是一种特殊形式。
答案 3 :(得分:-1)
在Scheme术语中,我们在整个标准报告中使用术语过程而不是功能。因此,由于这是关于方案方言,我将使用术语过程。
在标准#!racket
和#!r6rs
过程等热切语言中,在使用新的词法环境评估正文之前,先评估其参数。因此,if
和lambda
具有特殊的评估规则,而不是程序,特殊形式和宏是引入新语法的方式。
在像#!lazy
这样的懒惰语言中,需要进行球拍评估,因此许多以急切语言实现为宏/特殊形式的表单可以作为过程实现。例如。您可以使用if
将cond
作为一个程序,但不能使用cond
if
,因为这些术语本身会被视为访问时的表单,例如(cond (#t 'true-value))
会因为#t
不是程序而失败。 lambda
与参数列表有类似的问题。