如何让匿名函数在 Red/Rebol 中工作

How to make anonymous function work in Red/Rebol

在Red/Rebol中,括号()可以用来计算这样的表达式:

>> (1 + 2 3 + 4)
== 7

但是,当我使用括号计算匿名函数时,它失败了(请看下面的代码)。为什么?以及如何使匿名函数起作用?

>> (func [x y][x + y] 2 3)
== 3  ;;I think it should be 5.

PS:

>> 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.

Why?

原因很简单:您的代码中没有匿名函数。如果您将其内容扫描为数据,它将变得更加明显:

>> 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],需要两个块和 returns 一个函数;其余表达式是对自身求值的文字 23;最后一个表达式的结果总是以红色返回,所以你得到 3。这种情况下的括号是多余的。

>> 0 + 1 2 3
== 3
>> func [x y][x + y] 2 3
== 3

所以,如果你想匿名评估一个函数,你首先需要在函数构造函数的帮助下从 spec 和 body 块创建一个 function! 值(例如 func, functionhasdoes 或您自己编写的其他一些),然后才将其应用于参数。最常见的方法是 do reduce pattern:

>> do reduce [func [x y][x + y] 2 3]
== 5

reduce 计算块中的每个子表达式(func 创建一个 function!23 计算自身),并且 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 系列中的计算是“分层的”,可以这么说:作为函数结果返回的值如果适用则不会立即重新计算,而是需要从顶级调用中额外传递评估者(例如 doreduce)。