Haskell拓展篇三:Fix和Fixpoint

Posted Lambda小粽子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Haskell拓展篇三:Fix和Fixpoint相关的知识,希望对你有一定的参考价值。


今天带给大家的是关于Haskellfix函数的介绍。这个函数在我们初次接触是可能会觉得有些奇怪,不过它引入的递归思想,以及其真正的在递归中起到的作用是值得我们去深入了解的。 

首先,这个函数在Haskell中被定义在了Control.Monad.Fix中,所以为了使用和了解这个函数,我们必须首先引用这个库。 

根据这个函数类型,我们来尝试自己定义一下fix函数。

Haskell拓展篇三:Fix和Fixpoint

上下两种定义都是可以的,就直观而言第一种定义更容易被理解。下面我们就来尝试着展开这个函数,根据第一种定义,myFix f = f (myFix f) = f (f (myFix f)) … 这个函数会被一直这样展开下去,而第二种定义展开的方式也极其类似,即myFix f = x = f x = f (f x) = f (f (f x)) ….

关于这个fix函数的应用,我们可以尝试几个小例子:fix (1:), fix (“hello” ++), fix (1+) ... 这几个例子中,第一个和第二个会生成一个infinite长的list,由于Haskell存在惰性求值,所以我们可以用take函数将前面的element取出来,而第三个例子由于fix函数会一直运算(1+)导致函数最后无法得出结果。(通过这个例子也希望大家体会一下例子1,2和3的区别,同样是infinite,由于计算思想的不一样所以在惰性求值的情况下会出现不一样的结果)

通过fix函数,我们可以重新定义我们之前定义过的递归函数。如之前我们曾经定义的fib函数。

Haskell拓展篇三:Fix和Fixpoint

当然这种定义是我们最开始说的效率很低的那种定义,不过不妨碍我们以之作为例子来感受下fix函数。在这里,myFixtype(a -> a) -> a,根据类型替换我们得知,a :: Int -> Int,所以myFix接收的函数的类型应为((Int -> Int) -> Int -> Int)这样的类型,其实在这里f就是fib函数,但是在整个函数的运算中我们都没有用到fib函数本身。这就是如何通过fix函数来实现递归的方法。事实上fix函数本身的初衷就是在lambda演算中去定义递归函数。Haskell(和其他函数式编程语言)的思想来源就是lambda演算,所以在Haskell中,即使我们一般情况定义递归函数并没有用到fix的概念,在语义上来讲递归函数的实现其实是跟fix函数密不可分的。之后我们会再接触一些Haskell底层的原理和更多关于Lambda演算的知识,在这里不为大家详细解释了。

在使用fix函数的时候,还有一个很重要的概念就是不动点概念。在数学中,不动点被定义为,作为一个函数f的不动点xf(x) =  x,即不动点通过函数的映射后还等于它本身。而在Haskell中,这个思想也可以同样被应用在fix函数中,即fix函数并不是无限的计算下去,而是当f (fix f) = fix f时函数停止。

Haskell拓展篇三:Fix和Fixpoint

其实我们可以在这一步的基础上对函数进行进一步的改造,我们要求函数满足某 个条件(而不只是x == f x)的时候停止并输出条件,那么我们的定义可以是这样。

这就意味着,我们可以自己定义函数的停止条件。到了这一步大家有没有觉得很熟悉?没错,这就是我们平常用的递归函数,停止条件就是我们一直在说的basecase,所以这也就进一步说明了,fix函数的思想和递归函数之间紧密的关联性。这种fix函数的应用,在某些渐进函数上是十分常见的,当函数的结果趋近于某一个值,并且误差小于我们规定的范围时我们就可以让函数停止(如牛顿开方等),而这个误差值就可以被定义在我们的函数停止条件中。


今天的内容就到这里,之后我们会步入Haskell中级篇,开始接触Haskell中很重要的一块--Monad,敬请期待



注:本文为原创内容,转载请标明出处。



以上是关于Haskell拓展篇三:Fix和Fixpoint的主要内容,如果未能解决你的问题,请参考以下文章

Haskell AST Annotation with Fix

Haskell拓展篇:斐波那契数列

RabbitMQ消息中间件技术精讲10 高级篇三 幂等性保障不重复消费

将定点运算符翻译成 Haskell 语言

这段混淆的 Haskell 代码是如何工作的?

java架构《并发线程高级篇三》