2015-11-11 51 views
2

对于Racket编程语言,为什么lambda不被视为函数?例如,它不能被定义为像这样的高阶函数。 (define (my-lambda args body) (lambda args body))为什么lambda不是函数

回答

2

有你的问题是缺少一个关键的区别:

  1. lambda语法
  2. 程序是

A lambda form是表达式的一种形式,其值是一个过程。 “lambda是一个函数”的问题从一个类型错误开始,可以这么说,因为lambda和程序不在同一个世界中。

但是让我们把它放在一边。另一种看待这个问题的方式是通过评估规则来考虑它。默认方案的评价规则,一个过程参数的应用,可以在伪代码这样表示:

(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)))) 

英文:

  1. 评估所有子表达式在相同的环境作为“父母”。
  2. apply第一个结果(必须对程序进行评估)到其余列表中。

而现在,另一个原因lambda不能是一个过程,这个评价规则不为lambda表达式工作。特别是lambda的点数是不是马上评价它的身体!这一点,尤其是什么困扰着你my-lambda - 如果您尝试使用这种方式:

(my-lambda (x) (+ x x)) 

...中间的(x)必须立即评估为环境命名x程序的调用整个表达出现的地方。 (+ x x)也必须立即评估。

因此lambda需要自己的评估规则。作为巴西莱的回答指出,这通常是实现该计划系统实施一个原始的,但我们可以像这样勾画它的伪代码:

;;; 
;;; 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翻译)的许多计划书之一。例如参见this section of Structure and Interpretation of Computer programs

2

lambda需要为核心的语言特性(如ifletdefine是)方案,因为它是构建closure所以需要管理组的关闭或free variables(以某种方式将其在封闭结合)。

例如:

(define (translate d) (lambda (x) (+ d x))) 

当你调用或评估(translate 3)d是3,因此动态构造闭合应该记住,d势必3.顺便说一句,你一般都希望的(translate 3)(translate 7)结果是两个不同关闭共享一些共同代码(但具有d不同的绑定)。

阅读关于λ-calculus

解释所有细节都需要整本书。幸运的是,C. Queinnec写了这个,所以请阅读他的Lisp In Small Pieces这本书。

(如果你读法文,你能读这本书的最新法国version

又见Kernel programming language

另请参阅wikipage约evaluation strategy

PS。你可以和一些Lisp实现(特别是MELT和可能SBCL)做到这一点,将lambda定义为macro -e.g.这将扩展到以特定实现的方式构建某些闭包(但lambda不能被定义为函数)。

+0

Nitpick on“needs”,因为如果你有“lambda”,那么“if”可以只是一个宏,其中两个子句变成lambdas。我相信Clojure会这样做。 – ArtB

+0

它可能取决于按值调用vs按名称调用。事实上,在Lambda微积分*中,如果*(以及整数和算术)可以用lambda来定义。实际上,这对于实现Scheme的实用可用实现没有用处。 –

-1

在计划术语中,我们在整个标准报告中使用术语过程而不是函数。因此,由于这是关于方案方言,我将使用术语过程。

在像标准#!racket#!r6rs程序渴望语言得到身体与新的词法环境评估之前评估他们的论点。因此,由于iflambda具有特殊的评估规则,而不是特定的形式,而宏是引入新语法的方式。

#!lazy这样懒惰的语言可以根据需要进行球拍评估,因此许多以渴望语言实现为宏/特殊形式的表单可以作为过程实现。例如。您可以使用condif作为过程制作,但您无法使用if制作cond,因为术语本身将在访问时评估为表格,例如(cond (#t 'true-value))将失败,因为#t不是过程。 lambda与参数列表有类似的问题。

+4

我有点失望,有经验的Scheme程序员会犯这么多Haskeller的错误,使得懒惰的语言中不需要宏。这是不正确的。有一个原因模板Haskell存在。虽然模仿懒惰可以用宏来实现,但它们解决了一个更为普遍的问题。从DSL到完整编译时静态类型系统(例如Typed Racket)的所有内容都可以用宏来实现 - 懒惰无法实现这一点。 –

+0

@AlexisKing我已经采取了我所听到的理所当然的事,但我现在看到,只能用​​程序来替换宏,其中您要添加的唯一功能是控制参数的评估。像'cond'这样的表格可能就是我的陈述是错误的简单例子,所以我添加了它。我学到了一些新东西,我显然需要用'#!lazy'多玩一点:-) – Sylwester

2

函数调用(E0 E1 E2)这样

  1. E0被评估被评估,结果是(希望)函数f
  2. E1进行评价,其结果是一个值V1
  3. E2进行评价,其结果是一个值v2
  4. f函数体在其中 形式参数绑定到值v1v2的环境中进行评价。 请注意,所有表达式e0,e1和e2都在函数主体激活之前进行评估。

这意味着,当(/ 3 0)被评估时 - 在控制权移交给foo主体之前,函数调用如(foo #t 2(/ 3 0))将导致错误。

现在考虑特殊形式lambda。在(lambda (x) (+ x 1))这创建了一个变量x的函数,当调用值v将计算(+ v 1)

如果相反lambda是一个函数,则表示(x)(+ x 1)lambda的主体被激活之前被评估。现在(x)很可能会产生一个错误 - 因为(x)表示调用没有参数的函数x

简而言之:在控件传递给函数体之前,函数调用将始终评估所有参数。如果某些表达式不需要评估,则需要一个特殊的表单。

这里lambda是一种表单,它不会评估所有子表单 - 所以lambda需要是一种特殊形式。

相关问题