SML中的递归匿名函数

时间:2011-08-10 17:18:08

标签: recursion functional-programming anonymous-function sml

是否可以在SML中编写递归匿名函数?我知道我可以使用fun语法,但我很好奇。

我写过,作为我想要的一个例子:

val fact =
    fn n => case n of
                 0 => 1
               | x => x * fact (n - 1)

4 个答案:

答案 0 :(得分:14)

将匿名函数绑定到a时,匿名函数不再是匿名函数 变量。由于val rec只是fun的派生形式而没有 除了外观之外的差异,您也可以使用它来编写它 fun语法。您也可以在fn表达式中进行模式匹配 与case中一样,因为案例来自fn

所以简单来说,你可以把你的功能写成

val rec fact = fn 0 => 1
                | x => x * fact (x - 1)

但这与下面更具可读性(在我的意见中)完全相同

fun fact 0 = 1
  | fact x = x * fact (x - 1)

据我所知,使用编写代码只有一个原因 long 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

然后您可以使用以下代码计算fact 5,该代码使用匿名 函数表达教师功能,然后绑定结果 计算到res

val res =
    Y (fn fact =>
       fn 0 => 1
        | n => n * fact (n - 1)
      )
      5                       

定点代码和示例计算由MortenBrøns-Pedersen提供。


对George Kangas回答的更新回复:

  

在我所知的语言中,递归函数总是绑定到a   名称。方便和传统的方式由关键字提供   “定义”,或“让”,或“letrec”,......

根据定义,这是真实的。如果函数(递归与否)未绑定到名称,则它将是匿名的。

  

非常规,更匿名的方式是通过lambda绑定。

我没有看到匿名函数有什么非常规的,它们一直在SML中使用,实际上是在任何函数式语言中。它甚至开始出现在越来越多的命令式语言中。

  

Jesper Reenberg的回答显示了lambda绑定; “匿名”   函数被lambdas绑定到名称“f”和“fact”(称为   SML中的“fn”。

匿名函数实际上是匿名的(不是“匿名” - 没有引号),是的当然它会被绑定在范围中作为参数传递给它的函数。在任何其他情况下,语言将完全无用。调用map (fn x => x) [.....]时会发生完全相同的事情,在这种情况下,匿名身份函数仍然是匿名的。

匿名函数的“正常”定义(至少根据wikipedia),说它不能绑定到标识符,有点弱,应该包含“当前的隐含语句”环境”。

事实上,对于我的示例来说,这是正确的,就像在仅包含fun Y ...val res ..

的文件中使用-show-basis参数在mlton中运行它一样
val Y: (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b
val res: int32

从中可以看出,环境中没有绑定任何匿名函数。

  

较短的“lambdanonymous”替代品,需要OCaml推出   通过“ocaml -rectypes”:

(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中甚至远程可能的功能完成。

答案 1 :(得分:8)

您只需将rec放在val之后,就像在

中一样
val rec fact =
        fn n => case n of
                     0 => 1
                   | x => x * fact (n - 1)

Wikipedia在第一部分的顶部附近描述了这一点。

答案 2 :(得分:2)

let fun fact 0 = 1
      | fact x = x * fact (x - 1)
in
  fact
end

这是一个递归匿名函数。名称'fact'仅在内部使用。

某些语言(例如Coq)使用“fix”作为递归函数的原语,而某些语言(例如SML)使用recursive-let作为原语。这两个原语可以互相编码:

fix f => e   
  := let rec f = e in f end

let rec f = e ... in ... end 
  := let f = fix f => e ... in ... end 

答案 3 :(得分:-1)

在我所知的语言中,递归函数将始终绑定到名称。方便和传统的方式由“定义”,“让”或“letrec”等关键字提供......

非传统的,更加匿名的寻找方式是通过lambda绑定。 Jesper Reenberg的答案显示了lambda绑定; “匿名”函数通过lambdas绑定到名称“f”和“fact”(在SML中称为“fn”)。

较短的“lambdanonymous”替代方案,需要OCaml由“ocaml -rectypes”发起:

(fun f n -> f f n)
(fun f n -> if n = 0 then 1 else n * (f f (n - 1))
7;;

哪个产生7个! = 5040。