仅具有 lambda 表达式的阶乘函数
您可以仅使用 lambda 表达式自行创建的最透明、最优雅的阶乘函数是什么?
我的一个学生在伯克利参加了一个计划课程,并获得了仅使用 lambda 表达式(没有定义、let 或其他幂过程)创建阶乘函数的额外学分问题。我花了一段时间才解决,而且很复杂而且很难看。
几年后,我现在正在教授Scheme,我意识到我要把它作为对自己的挑战,并认为其他人也可能会欣赏它。
What is the most transparent and elegant factorial function you can create, on your own, using only lambda expressions?
One of my students took a Scheme class at Berkeley and was given this extra credit problem of creating the factorial function with only lambda expressions (no define, let, or other power procedures). It took me awhile to solve, and was complicated and ugly.
I am teaching Scheme now, a couple of years later, and I realized I was going to set it as a challenge for myself, and thought others might appreciate it as well.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这是一个(柯里化的)版本:
尾递归版本:
关于教堂数字:
Here's one (curried) version:
Tail-recursive version:
On Church numerals:
这是我能想到的最简单的尾递归版本:
很难在更少的空间中获得递归。自应用程序必须发生在某个地方,并且大多数按值调用语言(如Scheme)中的独立固定点都必须引入额外的 lambda 以避免自应用程序中的失控递归。
相反,我的解决方案和 Jeremiah 的解决方案将自我应用程序隐藏在Scheme 的短路
if
的一个分支中,从而以少得多的字符提供必要的递归。Here's the simplest tail-recursive version I can think of:
It's hard to get recursion in less space. The self-application has to happen somewhere, and most standalone fixpoints in a call-by-value language like Scheme have to introduce extra lambdas to avoid runaway recursion at the self-application.
Instead, my solution and Jeremiah's hide the self-application in one branch of Scheme's short-circuit
if
, giving the necessary recursion with far fewer characters.我几年前做的一个台词数量是我几年前的两倍,而且更难理解。
The one I did a couple of years ago had twice as many lines and was much harder to follow.
这是我之前在研究 Y-Combinator 时编写的代码。
Here's mine that I coded up before when wrapping my head around the Y-Combinator.
我首先在无类型 lambda 演算中编写解决方案,使用诸如 0?、true、false 等的顶级定义,定义使用教会编码。此实现假设多参数函数是柯里化的并且函数是部分应用的(如 Haskell)。
Church 编码自然数如下:
Church 布尔值
true
和false
定义如下定义了这些先决条件后,现在我们使用自我应用程序来定义阶乘函数以进行递归。这个定义是尾递归的。
从这里开始,我作弊并在
!
上执行了正常的顺序评估;这本质上是部分评估。为此,我必须删除U
的定义以防止发散,然后将其添加回来。这是由此产生的术语。它相当神秘(尽管如果没有翻译,我很难用手写出这么短的东西),所以我添加了注释来标识我仍然可以识别的部分。
乘法可能很难发现,但它确实存在。现在,为了测试它,我评估了应用于 3 的术语,即
(λf x.f (f (fx)))
。混合应用和混合正态评估都减少到正常项而不发散,产生λf x。 f (f (f (f (f (fx)))))
,或 6。其他归约策略要么发散(由于自我应用),要么不归约到正常形式。I first wrote the solution in the untyped lambda calculus, using top-level definitions for things like zero?, true, false, etc, defined using Church encodings. This implementation assumes that multi-argument functions are curried and that functions are partially applied (like Haskell).
Church encoding natural numbers looks like:
Church booleans
true
andfalse
are defined belowWith those pre-requisites defined, now we define the factorial function using self-application for recursion. This definition is tail-recursive.
From here, I cheated and performed normal order evaluation on
!
; this is essentially partial evaluation. For this to work, I had to remove the definition ofU
to prevent divergence, then add it back in after.Here's the resulting term. It is fairly cryptic (though I would've had difficulty writing anything this short by hand, without an interpreter) so I've added comments identifying the parts I can still recognize.
The multiplication might be hard to spot, but it's there. Now, to test it I evaluated the term applied to 3, or
(λf x. f (f (f x)))
. Hybrid applicative and hybrid normal evaluation both reduce to a normal term without diverging, yieldingλf x. f (f (f (f (f (f x)))))
, or 6. Other reduction strategies either diverge (due to the self application) or don't reduce to normal form.这是我不久前做的一个
它定义了所有的斐波那契数(当然,通过无限的教会对列表)
我确实走得更远,摆脱了 if、0、1、+ 和 -,但在无论如何,这些都需要从教堂数字来回转换。那时它变得很荒谬。
Here's one that I did a while back
It defines all of the fibonacci numbers (via a infinite list of church pairs, of course)
I did go even further, getting rid of if, 0, 1, +, and -, but in the end those were needed to convert back and forth from church numerals anyway. And it was getting ridiculous at that point.