SML 中的递归匿名函数
是否可以在 SML 中编写递归匿名函数?我知道我可以使用 fun
语法,但我很好奇。
我已经写了,作为我想要的例子:
val fact =
fn n => case n of
0 => 1
| x => x * fact (n - 1)
Is it possible to write recursive anonymous functions in SML? I know I could just use the fun
syntax, but I'm curious.
I have written, as an example of what I want:
val fact =
fn n => case n of
0 => 1
| x => x * fact (n - 1)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
当您将匿名函数绑定到
多变的。而且由于
val rec
只是fun
的派生形式,没有除了外观之外的差异,您也可以使用它来编写它
fun
语法。您也可以在fn
表达式中进行模式匹配如
case
中所示,因为 case 源自fn
。因此,就其简单性而言,您可以将函数编写为,
但这与下面的代码完全相同,更具可读性(在我看来)
据我认为,只有一个原因可以使用以下代码编写代码
long
val rec
,这是因为您可以更轻松地用注释和强制类型。例如,如果您之前看过 Haskell 代码并且
就像他们输入注释其函数的方式一样,你可以写一些东西
就像这样
正如 templatetypedef 提到的,可以使用定点来做到这一点
组合器。这样的组合器可能看起来像这样
,然后您可以使用下面的代码计算事实 5,该代码使用匿名
函数来表达功能函数,然后绑定结果
计算到
res
。定点代码和示例计算由 Morten Brøns-Pedersen 提供。
对乔治·坎加斯的回答的更新回复:
根据定义,这很简单。如果函数(无论是否递归)未绑定到名称,它将是匿名的。
我不明白匿名函数有什么非常规之处,它们一直在 SML 中使用,事实上在任何函数式语言中都是如此。它甚至开始出现在越来越多的命令式语言中。
匿名函数实际上是匿名的(不是“匿名”——没有引号),是的,它当然会被绑定在它作为参数传递的函数的范围中。在任何其他情况下,语言将完全没有用处。调用
map (fn x => x) [.....]
时会发生完全相同的事情,在本例中是匿名身份函数,实际上仍然是匿名的。匿名函数的“正常”定义(至少根据 维基百科),表示它不能绑定到标识符有点弱,应该包含“在当前环境中”的隐式声明。
对于我的示例来说,这实际上是正确的,正如通过在仅包含
fun Y ...
和val res ..< 的文件上使用 -show-basis 参数在 mlton 中运行它所看到的那样。 /code>
由此可以看出,没有任何匿名函数被绑定在环境中。
看来您完全误解了原始问题的想法:
简单的答案是肯定的。复杂的答案是(除其他外?)使用定点组合器完成的示例,而不是使用另一种语言完成的“匿名”(无论这是什么意思)示例,使用 SML 中根本不可能实现的功能。
The anonymous function aren't really anonymous anymore when you bind it to a
variable. And since
val rec
is just the derived form offun
with nodifference other than appearance, you could just as well have written it using
the
fun
syntax. Also you can do pattern matching infn
expressions as wellas in
case
, as cases are derived fromfn
.So in all its simpleness you could have written your function as
but this is the exact same as the below more readable (in my oppinion)
As far as I think, there is only one reason to use write your code using the
long
val rec
, and that is because you can easier annotate your code withcomments and forced types. For examples if you have seen Haskell code before and
like the way they type annotate their functions, you could write it something
like this
As templatetypedef mentioned, it is possible to do it using a fixed-point
combinator. Such a combinator might look like
And you could then calculate
fact 5
with the below code, which uses anonymousfunctions to express the faculty function and then binds the result of the
computation to
res
.The fixed-point code and example computation are courtesy of Morten Brøns-Pedersen.
Updated response to George Kangas' answer:
Trivially true by definition. If the function (recursive or not) wasn't bound to a name it would be anonymous.
I don't see what unconventional there is about anonymous functions, they are used all the time in SML, infact in any functional language. Its even starting to show up in more and more imperative languages as well.
The anonymous function is in fact anonymous (not "anonymous" -- no quotes), and yes of course it will get bound in the scope of what ever function it is passed onto as an argument. In any other cases the language would be totally useless. The exact same thing happens when calling
map (fn x => x) [.....]
, in this case the anonymous identity function, is still in fact anonymous.The "normal" definition of an anonymous function (at least according to wikipedia), saying that it must not be bound to an identifier, is a bit weak and ought to include the implicit statement "in the current environment".
This is in fact true for my example, as seen by running it in mlton with the -show-basis argument on an file containing only
fun Y ...
and theval res ..
From this it is seen that none of the anonymous functions are bound in the environment.
It seems that you have completely misunderstood the idea of the original question:
And the simple answer is yes. The complex answer is (among others?) an example of this done using a fix point combinator, not a "lambdanonymous" (what ever that is supposed to mean) example done in another language using features not even remotely possible in SML.
您所要做的就是将
rec
放在val
之后,如Wikipedia 在第一部分顶部附近对此进行了描述。
All you have to do is put
rec
afterval
, as inWikipedia describes this near the top of the first section.
这是一个递归匿名函数。名称“fact”仅在内部使用。
某些语言(例如 Coq)使用“fix”作为递归函数的原语,而某些语言(例如 SML)使用 recursive-let 作为原语。这两个原语可以相互编码:
This is a recursive anonymous function. The name 'fact' is only used internally.
Some languages (such as Coq) use 'fix' as the primitive for recursive functions, while some languages (such as SML) use recursive-let as the primitive. These two primitives can encode each other:
在我所知道的语言中,递归函数总是与名称绑定。方便且传统的方式是通过诸如“define”、“let”或“letrec”之类的关键字提供的。
非常规、更加匿名的方式是通过 lambda 绑定。 Jesper Reenberg 的答案显示了 lambda 结合; “匿名”函数通过 lambda 绑定到名称“f”和“fact”(在 SML 中称为“fn”)。
更短的“lambdanonymous”替代方案,需要由“ocaml -rectypes”启动的 OCaml:
产生 7! = 5040。
In languages I know, a recursive function will always get bound to a name. The convenient and conventional way is provided by keywords like "define", or "let", or "letrec",...
The unconventional, more anonymous looking, way is by lambda binding. Jesper Reenberg's answer shows lambda binding; the "anonymous" function gets bound to the names "f" and "fact" by lambdas (called "fn" in SML).
A shorter "lambdanonymous" alternative, which requires OCaml launched by "ocaml -rectypes":
Which produces 7! = 5040.