在SML中是否可以编写递归匿名函数?我知道我可以使用fun
语法,但我很好奇。
我已经写了一个例子来说明我想要的:
val fact =
fn n => case n of
0 => 1
| x => x * fact (n - 1)
在SML中是否可以编写递归匿名函数?我知道我可以使用fun
语法,但我很好奇。
我已经写了一个例子来说明我想要的:
val fact =
fn n => case n of
0 => 1
| x => x * fact (n - 1)
当将匿名函数绑定到变量时,它们不再是真正的匿名函数。而且,由于val rec
只是fun
的派生形式,除了外观没有任何区别,因此您也可以使用fun
语法来编写它。此外,在fn
表达式和case
中都可以进行模式匹配,因为case
是从fn
派生出来的。
因此,您可以将函数简化为:
val rec fact = fn 0 => 1
| x => x * fact (x - 1)
但在我看来,这段代码与以下更易读的代码完全相同。
fun fact 0 = 1
| fact x = x * fact (x - 1)
我认为只有一个使用长val rec
来编写代码的原因,那就是你可以更容易地使用注释和强制类型进行注解。例如,如果你之前看过Haskell代码并且喜欢他们对函数进行类型注解的方式,那么你可以像这样编写它:
val rec fact : int -> int =
fn 0 => 1
| x => x * fact (x - 1)
正如templatetypedef所提到的,使用固定点组合器可以实现这一点。这样的组合器可能看起来像这样
fun Y f =
let
exception BlackHole
val r = ref (fn _ => raise BlackHole)
fun a x = !r x
fun ta f = (r := f ; f)
in
ta (f a)
end
你可以使用下面的代码计算5的阶乘
,该代码使用匿名函数表达式表示faculty函数,然后将计算结果绑定到res
。
val res =
Y (fn fact =>
fn 0 => 1
| n => n * fact (n - 1)
)
5
该固定点代码及示例计算由Morten Brøns-Pedersen提供。
在我所知道的语言中,递归函数总会被绑定到一个名称上。方便和常规的方式是通过像“define”、“let”或“letrec”这样的关键字提供的。
显然是根据定义而言的。如果(无论是否递归)函数未绑定到名称上,则它将是匿名的。
非传统的、更具匿名性的方式是通过lambda绑定实现的。
我不明白匿名函数有什么不传统之处,它们在SML中经常使用,事实上在任何函数式语言中都是如此。甚至在越来越多的命令式语言中也开始出现了。
Jesper Reenberg的答案展示了 lambda 绑定;“匿名”函数通过 lambdas ( 在 SML 中称为 "fn" )绑定到"f"和"fact"这两个名称上。
这个匿名函数实际上是匿名的(不是“匿名”——没有引号),当然它将在作为参数传递给其他函数时被绑定在作用域中。否则,该语言将完全无用。当调用map (fn x => x) [.....]
时,发生的正是同样的事情,在这种情况下,匿名身份函数仍然是匿名的。
“匿名”函数的“正常”定义(至少根据维基百科),说它不必绑定到标识符上,有点薄弱,应包括隐含的声明“在当前环境中”。
对于我的示例来说,这实际上是正确的,通过在只包含fun Y ...
和val res ..
的文件上使用mlton并带上-show-basis参数即可看到。
val Y: (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b
val res: int32
由此可见,环境中没有绑定任何匿名函数。
另一种更短的“lambdanonymous”选择,需要通过“ocaml -rectypes”启动OCaml:
似乎你完全误解了原问题的意义:
(fun f n -> f f n) (fun f n -> if n = 0 then 1 else n * (f f (n - 1)) 7;; Which produces 7! = 5040.
简单的答案是可以。复杂的答案是(包括其他内容)使用不动点组合子的示例,而不是在另一种语言中使用甚至在SML中也无法实现的功能编写的“lambdanonymous”示例。在SML中是否可能编写递归匿名函数?
val rec
是核心语言的一部分,而 fun
关键字则是派生形式。我手头没有定义,但请参见 [http://homepages.inf.ed.ac.uk/stg/NOTES/notes.pdf] 的第 3.4 节(这是我在谷歌上找到的最好的资料)。 - Jesper.Reenberg您只需在 val
后面加上 rec
,例如:
val rec fact =
fn n => case n of
0 => 1
| x => x * fact (n - 1)
维基百科 在第一节的开头进行了描述。
let fun fact 0 = 1
| fact x = x * fact (x - 1)
in
fact
end
这是一个递归匿名函数。名称“fact”仅在内部使用。
一些语言(如Coq)使用“fix”作为递归函数的原语,而一些语言(如SML)则使用递归let作为原语。这两个原语可以互相编码:
fix f => e
:= let rec f = e in f end
let rec f = e ... in ... end
:= let f = fix f => e ... in ... end
(fun f n -> f f n)
(fun f n -> if n = 0 then 1 else n * (f f (n - 1))
7;;
这将产生 7! = 5040。