我如何使用修复程序,它是如何工作的?

我对fix的文档有点困惑(尽pipe我想我明白它现在应该做什么),所以我查看了源代码。 这让我更加困惑:

 fix :: (a -> a) -> a fix f = let x = fx in x 

这究竟是如何返回一个固定的点?

我决定尝试在命令行:

 Prelude Data.Function> fix id ... 

它挂在那里。 现在公平地说,这是我的旧的macbook,这是慢的。 然而,这个函数的计算花费不算太高,因为任何传递给id的东西都会返回相同的结果(更不要说它没有CPU时间了)。 我究竟做错了什么?

你没有做错什么。 fix id是一个无限循环。

当我们说fix返回一个函数的最小不动点时,我们是指在领域理论意义上。 所以fix (\x -> 2*x-1) 不会返回1 ,因为尽pipe1是该函数的一个固定点,但它并不是域中sorting的最小者

我不能仅仅在一两个段落中描述域的sorting,所以我会把你引向上面的域的理论链接。 这是一个很好的教程,易于阅读,相当有启发性。 我强烈推荐它。

对于10,000英尺的视图来说, fix是一个高阶函数,它编码了recursion的思想。 如果你有这样的expression:

 let x = 1:x in x 

其结果是无限列表[1,1..] ,你可以用fix来说同样的事情:

 fix (\x -> 1:x) 

(或简单地fix (1:) ),它说我find(1:)函数的一个固定点,IOW一个值x ,使得x = 1:x …就像我们上面定义的那样。 从定义中可以看出, fix只不过是这个想法 – 将recursion封装到一个函数中。

这是一个recursion的真正的一般概念 – 你可以用这种方式编写任何recursion函数(只要它不是极其罕见的多态recursion …即使这样我不确定它是不是使用fix可编码)。 所以例如典型的斐波那契函数:

 fib n = if n < 2 then n else fib (n-1) + fib (n-2) 

可以这样写:

 fib = fix (\f -> \n -> if n < 2 then n else f (n-1) + f (n-2)) 

练习:展开fix的定义,以显示这两个fib定义是等价的。

但是要充分理解,请阅读域理论。 这真的很酷的东西。

我并不是说明白这一点,但是如果这有助于任何人……那么yippee。

考虑fix的定义。 fix f = let x = fx in x 。 令人难以置信的是x被定义为fx 。 但是想一下,

 x = fx 

既然x = fx,那么我们可以用右边的x的值代替,对吧? 所以…

 x = f . f $ x -- or x = f (fx) x = f . f . f $ x -- or x = f (f (fx)) x = f . f . f . f . f . f . f . f . f . f . f $ x -- etc. 

所以诀窍是,为了终止, f必须生成某种结构,以便后面的f可以模式匹配该结构并终止recursion,而实际上并不关心其参数(?)的完整“值”

当然,除非你想像做一个无限的列表那样做,正如卢奎所说的那样。

TomMD的因子解释是很好的。 Fix的types签名是(a -> a) -> a(\recurse d -> if d > 0 then d * (recurse (d-1)) else 1)的types签名是(b -> b) -> b -> b ,换句话说, (b -> b) -> (b -> b) 。 所以我们可以说a = (b -> b) 。 这样,修复就是使用我们的函数,它是a -> a ,或者真的, (b -> b) -> (b -> b) ,并返回a的结果,换句话说, b -> b换句话说,另一个function!

等等,我认为它应该返回一个固定的点…不是一个函数。 那么它的确有点(因为函数是数据)。 你可以想象它给了我们find一个阶乘的明确function。 我们给它一个不知道如何recursion的函数(因此它的一个参数是一个用于recursion的函数),并且fix它教会如何recursion。

请记住我是怎么说f必须产生某种结构,以便以后的f可以匹配并终止? 那么这不完全正确,我猜。 TomMD说明了我们如何扩展x来应用这个function,并向基本情况迈进。 为了他的function,他使用了一个if / then,这就是导致终止的原因。 经过反复的replace,整个定义的部分定义最终停止以x来定义,那就是当它是可计算和完整的时候。

您需要一种方法来终止固定点。 扩展你的例子很明显,它不会完成:

 fix id --> let x = id x in x --> id x --> id (id x) --> id (id (id x)) --> ... 

这是一个真正的例子,我使用修复(注意我不经常使用修复,可能是累了/不用担心可读代码,当我写这个):

 (fix (\fh -> if (pred h) then f (mutate h) else h)) q 

跆拳道,你说! 那么,是的,但这里有一些真正有用的观点。 首先,你的第一个fix参数通常应该是一个“recursion”情况的函数,第二个参数是要采取行动的数据。 这是一个命名的函数相同的代码:

 getQ h | pred h = getQ (mutate h) | otherwise = h 

如果你仍然感到困惑,那么factorial就是一个简单的例子:

 fix (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) 5 -->* 120 

注意评估:

 fix (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) 3 --> let x = (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x in x 3 --> let x = ... in (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x 3 --> let x = ... in (\d -> if d > 0 then d * (x (d-1)) else 1) 3 

哦,你刚才看到了吗? 那个x成了我们then分支的一个function。

 let x = ... in if 3 > 0 then 3 * (x (3 - 1)) else 1) --> let x = ... in 3 * x 2 --> let x = ... in 3 * (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x 2 --> 

在上面你需要记住x = fx ,因此x 2的两个参数在最后而不是2

 let x = ... in 3 * (\d -> if d > 0 then d * (x (d-1)) else 1) 2 --> 

我会在这里停下来!

一个明确的定义是你可以重新发明修补程序呢!

我的理解是,它为这个函数find了一个值,这样它就输出了你给它的相同的东西。 值得一提的是,它总是会selectundefined(或者是一个无穷循环,在haskell中,undefined和infinite循环都是一样的),或者是其中最没有定义的东西。 例如,用id,

 λ <*Main Data.Function>: id undefined *** Exception: Prelude.undefined 

正如你所看到的,undefined是一个固定的点,所以fix会select。 如果你做(\ x-> 1:x)。

 λ <*Main Data.Function>: undefined *** Exception: Prelude.undefined λ <*Main Data.Function>: (\x->1:x) undefined [1*** Exception: Prelude.undefined 

所以fix不能select未定义。 使它更多地连接到无限循环。

 λ <*Main Data.Function>: let y=y in y ^CInterrupted. λ <*Main Data.Function>: (\x->1:x) (let y=y in y) [1^CInterrupted. 

再次,略有不同。 那么固定点是什么? 让我们尝试repeat 1

 λ <*Main Data.Function>: repeat 1 [1,1,1,1,1,1, and so on λ <*Main Data.Function>: (\x->1:x) $ repeat 1 [1,1,1,1,1,1, and so on 

这是相同的! 由于这是唯一的固定点, fix必须解决。 抱歉, fix ,没有无限循环或未定义你。