问题描述
在 Red/Rebol 中,括号 ()
可用于计算如下表达式:
>> (1 + 2 3 + 4)
== 7
但是,当我使用括号计算匿名函数时,它失败(请参阅以下代码)。为什么?以及如何使匿名函数工作?
>> (func [x y][x + y] 2 3)
== 3 ;;I think it should be 5.
附注:
>> do func [x y] [x + y]
== func [x y][x + y]
>> type? do func [x y] [x + y]
== function!
>> (do func [x y] [x + y]) 2 3
== 3 ;; why does this anonymous function still not work?
>> ((do func [x y] [x + y]) 2 3)
== 3 ;; This does not work too.
解决方法
为什么?
原因很简单:你的代码中没有匿名函数。如果您将其内容作为数据进行扫描,它将变得更加明显:
>> 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!
,而不是一个函数。但是,一旦评估,它将返回一个函数,其文字形式看起来完全相同。 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
因此,如果您想匿名评估一个函数,您首先需要借助函数构造函数(例如 function!
、func
、function
、has
或您自己编写的其他一些内容),然后才将其应用于参数。最常见的方法是 does
模式:
do reduce
>> do reduce [func [x y][x + y] 2 3]
== 5
对块中的每个子表达式求值(reduce
创建一个 func
、function!
和 2
对自身求值),并且 {{1 }} 然后解释它(将匿名函数应用于两个参数)。
这里有一些其他方法,以便您可以掌握这个概念:
3
至于为什么 do
以这种方式工作:这是设计使然,以防止可变参数函数调用。您可以详细了解其原理here。
Rebol/Red 中的所有函数都具有固定的元数,并且仅根据需要计算尽可能多的表达式;返回调用点并使用剩余参数的函数(例如在 Lisp 中)将违反此规则。
>> 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
作为历史上的好奇心,Rebol3 有一个 do func [x y][x + y]
改进,它允许函数做到这一点,后来由于我上面概述的原因而改进了 removed。
Rebol 系列中的求值是“分层的”,可以这么说:作为函数结果返回的值如果适用,则不会立即重新求值,而是需要从顶层调用到评估器(例如 ((lambda (x y)(+ x y)) 1 2)
=> 3
或 return/redo
)。