我是fixed-point combinators世界的新手,我猜他们已经习惯了匿名的lambdas,但我还没有真正使用它们,甚至无法完全绕过它们。
我在Javascript中看到Y-combinator的示例,但无法成功运行它。
这里的问题是,有人可以直截了当地回答:
奖励积分: 如果示例不只是一种语言,最好还是 Clojure 。
更新
我已经能够在Clojure中找到一个简单的例子,但仍然觉得很难理解Y-Combinator本身:
(defn Y [r]
((fn [f] (f f))
(fn [f]
(r (fn [x] ((f f) x))))))
虽然这个例子很简洁,但我发现很难理解函数中发生了什么。提供的任何帮助都很有用。
答案 0 :(得分:11)
假设您想编写阶乘函数。通常,你会把它写成像
这样的东西function fact(n) = if n=0 then 1 else n * fact(n-1)
但是它使用显式递归。如果你想使用Y-combinator,你可以先将事实抽象为
function factMaker(myFact) = lamba n. if n=0 then 1 else n * myFact(n-1)
这需要一个参数(myFact),它调用的是“真实”事实本身就会调用它。我将这种功能称为“Y-ready”,这意味着它已经准备好被送到Y-combinator。
Y-combinator使用factMaker来构建与“真实”事实相当的东西。
newFact = Y(factMaker)
为什么要这么麻烦?两个原因。第一个是理论上的:如果我们可以使用Y-combinator“模拟”它,我们真的不需要递归。
第二个更务实。有时我们想用一些额外的代码来包装每个函数调用来进行日志记录或分析或记忆或许多其他事情。如果我们尝试对“真实”事实这样做,那么额外的代码只会被调用到原始的事实调用,而不是所有的递归调用。但是如果我们想要为每个调用执行此操作,包括所有递归调用,我们可以执行类似
的操作loggingFact = LoggingY(factMaker)
其中LoggingY是引入日志记录的Y组合器的修改版本。请注意,我们根本不需要更改factMaker!
所有这些都是为什么Y-combinator的重要性,而不是Y的特定实现如何工作的详细解释(因为有许多不同的方法来实现Y)。
答案 1 :(得分:3)
回答你关于Y以外的定点组合子的第二个问题。有无数的标准定点组合器,即满足等式的组合器fix
fix f = f (fix f)
还有许多非标准定点组合器,它们满足等式
fix f = f (f (fix f))
等。标准的定点组合器是递归可枚举的,但非标准的不是。请参阅以下网页以获取示例,参考和讨论。 http://okmij.org/ftp/Computation/fixed-point-combinators.html#many-fixes
答案 2 :(得分:0)
关于问题的“……内部正在发生什么”部分:我只是写了一个非常简洁,扎实的解释,根本没有提到不动点的概念:https://github.com/ggandor/y-combinator-tutorial 。在那里对这些步骤进行了非常清晰的解释,并用Clojure代码进行了说明。
如果我只想强调一件事,那就是Y组合器实际上将自身的身体传递给参数函数(克里斯·冈崎(Chris Okasaki)的answer中的<audio id="myAudio" src="" preload="auto"></audio>
<button id="play-pause">PLAY/PAUSE SOUND</button>
factMaker
(在Clojure代码段中),其中已经绑定(即容易获得)r
。因此,factMaker
可以在到达递归点时启动催生它的相同过程。