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

问题描述

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

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

因此,如果您想匿名评估一个函数,您首先需要借助函数构造函数(例如 function!funcfunctionhas 或您自己编写的其他一些内容),然后才将其应用于参数。最常见的方法是 does 模式:

do reduce

>> do reduce [func [x y][x + y] 2 3] == 5 对块中的每个子表达式求值(reduce 创建一个 funcfunction!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)。