为什么?
原因很简单:您的代码中没有匿名函数。如果您将其内容扫描为数据,它会变得更加明显:
>> code: quote (func [x y][x + y] 2 3)
== (func [x y] [x + y] 2 3)
>> forall code [probe type? code/1]
word!
block!
block!
integer!
integer!
== integer!
也就是说:func [x y][x + y]是一个word!后跟2个block!s,不是一个函数。但是,一旦评估,它将返回一个函数,其字面形式看起来完全相同。 func 本身就是一个创建另一个函数的函数(又名函数构造函数)。
>> type? probe func [x y][x + y]
func [x y][x + y]
== function!
在您的示例中,3 正是出于这个原因返回:解释器按顺序评估每个表达式;第一个表达式是一个函数调用func [x y][x + y],它接受两个块并返回一个函数;剩下的表达式是文字 2 和 3 ,它们对自己求值;最后一个表达式的结果总是以红色返回,所以你得到3。这种情况下的括号是多余的。
>> 0 + 1 2 3
== 3
>> func [x y][x + y] 2 3
== 3
所以,如果你想匿名评估一个函数,你首先需要在函数构造函数的帮助下从 spec 和 body 块中创建一个 function! 值(例如func、function、has、@ 987654339@ 或您自己编写的其他文件),然后才将其应用于参数。最常见的做法是do reduce 模式:
>> do reduce [func [x y][x + y] 2 3]
== 5
reduce 计算块中的每个子表达式(func 创建一个 function!、2 和 3 计算自己),然后 do 解释它(将匿名函数应用于两个论据)。
这里有一些其他方法,以便您掌握这个概念:
>> do compose [(func [x y][x + y]) 2 3]
== 5
>> do head insert [2 3] function [x y][x + y]
== 5
>> do reverse append [3 2] make function! [[x y][x + y]]
== 5
>> λ: func [spec body code][do compose [(func spec body) (code)]]
== func [spec body code][do compose [(func spec body) (code)]]
>> λ [x y][x + y][2 3]
== 5
至于为什么do func [x y][x + y] 会这样工作:这是设计使然,以防止可变参数函数调用。你可以阅读更多关于它的基本原理here。
Rebol/Red 中的所有函数都有一个固定的元数,并且只计算所需数量的表达式;返回调用站点并使用剩余参数的函数(例如在 Lisp 中)将违反此规则。
((lambda (x y)(+ x y)) 1 2)
=> 3
作为一个历史奇闻,Rebol3 有一个 return/redo 改进,它允许函数做到这一点,由于我上面概述的原因,后来是 removed。
Rebol 系列中的评估是“分层的”,可以这么说:作为结果返回的值如果适用,则不会立即重新评估,而是需要从顶层调用额外传递到评估者(例如do 或reduce)。