为什么lambda不是一个函数

时间:2015-11-11 17:49:53

标签: functional-programming racket

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

4 个答案:

答案 0 :(得分:2)

lambda需要是Scheme中的核心语言功能(例如ifletdefine),因为它正在构建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)

您的问题缺失了一个关键区别:

  1. lambda语法
  2. 程序
  3. 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))))
    

    英文:

    1. 评估与" parent"在同一环境中的所有子表达式。
    2. apply第一个结果(必须已经评估过程)到其余的列表中。
    3. 现在,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)的评估方式如下

    评估
  1. e0,结果(希望是)函数f
  2. 评估e1,结果为值v1
  3. 评估
  4. e2,结果为值v2
  5. f的函数体在其中的环境中进行评估 形式参数绑定到值v1v2。 请注意,在激活函数体之前,将评估所有表达式e0,e1和e2。
  6. 这意味着像(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过程等热切语言中,在使用新的词法环境评估正文之前,先评估其参数。因此,iflambda具有特殊的评估规则,而不是程序,特殊形式和宏是引入新语法的方式。

在像#!lazy这样的懒惰语言中,需要进行球拍评估,因此许多以急切语言实现为宏/特殊形式的表单可以作为过程实现。例如。您可以使用ifcond作为一个程序,但不能使用cond if,因为这些术语本身会被视为访问时的表单,例如(cond (#t 'true-value))会因为#t不是程序而失败。 lambda与参数列表有类似的问题。