对于Racket编程语言,为什么lambda不被视为函数?例如,它不能被定义为像这样的高阶函数。 (define (my-lambda args body) (lambda args body))
为什么lambda不是函数
回答
有你的问题是缺少一个关键的区别:
lambda
是语法。- 程序是值。
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))))
英文:
- 评估所有子表达式在相同的环境作为“父母”。
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。
lambda
需要为核心的语言特性(如if
,let
,define
是)方案,因为它是构建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
不能被定义为函数)。
在计划术语中,我们在整个标准报告中使用术语过程而不是函数。因此,由于这是关于方案方言,我将使用术语过程。
在像标准#!racket
和#!r6rs
程序渴望语言得到身体与新的词法环境评估之前评估他们的论点。因此,由于if
和lambda
具有特殊的评估规则,而不是特定的形式,而宏是引入新语法的方式。
像#!lazy
这样懒惰的语言可以根据需要进行球拍评估,因此许多以渴望语言实现为宏/特殊形式的表单可以作为过程实现。例如。您可以使用cond
将if
作为过程制作,但您无法使用if
制作cond
,因为术语本身将在访问时评估为表格,例如(cond (#t 'true-value))
将失败,因为#t
不是过程。 lambda
与参数列表有类似的问题。
我有点失望,有经验的Scheme程序员会犯这么多Haskeller的错误,使得懒惰的语言中不需要宏。这是不正确的。有一个原因模板Haskell存在。虽然模仿懒惰可以用宏来实现,但它们解决了一个更为普遍的问题。从DSL到完整编译时静态类型系统(例如Typed Racket)的所有内容都可以用宏来实现 - 懒惰无法实现这一点。 –
@AlexisKing我已经采取了我所听到的理所当然的事,但我现在看到,只能用程序来替换宏,其中您要添加的唯一功能是控制参数的评估。像'cond'这样的表格可能就是我的陈述是错误的简单例子,所以我添加了它。我学到了一些新东西,我显然需要用'#!lazy'多玩一点:-) – Sylwester
函数调用(E0 E1 E2)这样
- E0被评估被评估,结果是(希望)函数f
- E1进行评价,其结果是一个值V1
- E2进行评价,其结果是一个值v2
- 的
f
函数体在其中 形式参数绑定到值v1
和v2
的环境中进行评价。 请注意,所有表达式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
需要是一种特殊形式。
- 1. 这是什么lambda函数做
- 2. 什么是lambda函数的类型?
- 3. lambda函数有什么用?
- 4. 什么是(lambda lambda lambda)?
- 5. 为什么这是一个使用lambda函数的属性?
- 6. 为什么不引用lambda?
- 7. 为什么我不能在递归lambda函数中使用auto?
- 8. 为按钮指定一个函数 - lambda有什么不同?
- 9. 为什么这里不允许使用lambda函数?
- 10. 为什么我不能在SERVICE_TABLE_ENTRY中存储lambda函数?
- 11. 为什么DynamoDBStream不能并行触发lambda函数?
- 12. 为什么我不能在C++中捕获递归lambda函数?
- 13. 为什么我的C++ lambda函数不能被捕获?
- 14. lambda函数不是逻辑
- 15. 为什么CanvasRenderingContext2D.createLinearGradient()不是静态函数?
- 16. 为什么.val()不是函数?
- 17. 为什么Firebug说toFixed()不是函数?
- 18. 为什么显示res.login不是函数?
- 19. 为什么javascript getTime()不是函数?
- 20. 为什么说xxx不是函数
- 21. 为什么它说loc不是函数
- 22. 为什么“TypeError:f不是函数”?
- 23. 为什么bindCallback不是函数?
- 24. 为什么addListener不是一个函数?
- 25. 为什么用functor而不是函数指针来实现C++ lambda?
- 26. 为什么这个lambda函数不是打字稿中的错误?
- 27. 为什么不将函数值传递给lambda函数并进行评估?
- 28. 这是lambda吗?如果不是什么?
- 29. 这个lambda函数做什么?
- 30. AWS lambda函数使用什么ip?
Nitpick on“needs”,因为如果你有“lambda”,那么“if”可以只是一个宏,其中两个子句变成lambdas。我相信Clojure会这样做。 – ArtB
它可能取决于按值调用vs按名称调用。事实上,在Lambda微积分*中,如果*(以及整数和算术)可以用lambda来定义。实际上,这对于实现Scheme的实用可用实现没有用处。 –