Haskell:如何获得函数的修复点?

时间:2014-02-05 02:07:19

标签: haskell

函数f的定点是值x,使得f(x)= x。编写一个函数修复函数,它接受函数f并返回其定点。

例如:伪代码如下:

f(x)= 
if (x=f(x)) return x
      else return f(f(x))

如何使用Haskell编写它?

4 个答案:

答案 0 :(得分:5)

从应用程序的角度来看,有很多种固定点。例如,我想区分逻辑固定点和分析固定点。该主题中的大多数答案都讨论了逻辑修复点。它可以在Haskell中写得非常漂亮如下

fix :: (a -> a) -> a
fix f = x where x = f x

甚至

fix :: (a -> a) -> a
fix f = f (fix f)

这种逻辑fix最终成为讨论和引入语言递归的一种自然方式。

分析修正常常出现在数值计算中,并且有一些不同但相关的含义。我们将从类型开始。

fixa :: (a -> a -> Bool) -> (a -> a) -> a -> Int -> a

这显然比简单的fix更复杂,因为它代表了守卫的血统。让我们开始编写fixa来为这些参数命名

fixa ok iter z n

目标是重复将iter应用于起始点z,直到n,正整数达到0ok current previous为{{ 1}}。这个实现几乎与这里的散文完全一样。

True

像这样的函数的值是我们可以用它来做迭代数值算法,比如Newton's Method

fixa ok iter z 0 = z
fixa ok iter z n = loop z n where
  loop z n = 
    let next = iter z
        in if (ok next z)
          then next
          else loop next (n-1)

我们还可以通过使用Haskell的惰性评估来吐出一个懒惰的结果列表而不仅仅是最后一点来大大改善它。当我们这样做时,我们不再需要手动循环计数器,因为由消费者决定如何管理这些改进列表。

newton :: (Double -> Double) -> (Double -> Double) -> Double -> Double
newton f f' z = fixa (\a b -> a - b < 1e-6) (\x -> x - f x / f' x) z 1000

事实上,我们不再需要fixaList :: (a -> a -> Bool) -> (a -> a) -> a -> [a] fixaList ok iter z = loop z where loop z = let next = iter z in if (ok next z) then cycle next -- we'll return next forever else z : loop next fixa ok iter z n = fixaList ok iter z !! n 测试,也可以留给消费者

ok

现在fixaList :: (a -> a) -> a -> [a] fixaList iter z = loop z where loop z = z : loop (iter z) fixa iter z n = take n (fixaList iter z) 看起来有点像fixaList

fix

事实上,我们可以将fix f = x where x = f x fixaList iter z = loop z where loop z = z : loop (iter z) 视为专业fixaList并使用fix来撰写

fix

这是一个很长的说法,逻辑固定点比分析固定点更强大。

答案 1 :(得分:3)

  

如何使用Haskell编写它?

让我们从应用程序的最后开始。所以我们希望编写递归匿名函数,例如因子,因此我们需要定点组合子。期望的结果:

fac' :: Integer -> Integer
fac' = fix fac

其中 fix 是定点组合子而 fac 是我们的阶乘组合子(不是那么匿名,但这只是为了方便)。

现在让我们定义fac。

fac :: (Integer -> Integer) -> Integer -> Integer
fac _ 1 = 1               -- Fixed point case
fac f x = x * f (x - 1)

第一个参数 f 是对fac本身的引用。现在我们可以开始推理修复。让我们从签名开始吧。在我们的示例中, fix 需要 fac 并返回一些 Integer - &gt;整数组合子。所以我们的签名如下(考虑修复可以处理任意类型的函数):

fix :: ((a -> a) -> a -> a) -> a -> a
fix f = ???

让我们来定义它。我们必须返回一些类型为 a - &gt;的 x 组合子。一个喂养一些类型的组合器 a - &gt; a f 。那么,最明显的解决方案是:

fix f = x where x = f x

这是一个实际的定义。但它的签名非常复杂......请注意,可以替换 a - &gt; a by y 。然后我们得到

fix :: (y -> y) -> y
fix f = x where x = f x

这接近标准库定义。它为什么有效?考虑我们的 fac 定义的第一个案例。我们不在那里使用第一个参数,因此懒惰会阻止在固定点处的无限循环。

答案 2 :(得分:2)

从技术上讲,它可以实现为

fix f x = let x' = f x in if x == x' then x else fix f x'

你试着看到

fix (\x -> (x + 5) `div` 2) 12345

返回5

print $ fix (\x -> (x + 3 / x) / 2) 12345

返回1.7320508075688772

答案 3 :(得分:1)

给定函数ffix f的值是多少?有点x x = f x。用Haskell替换上面的英语词典,我们得到了

fix f = x where x = f x

尝试fix (1:)fix (const 42)

就是这样。现在,根据定义,x返回的任何值fix f等于f x。那是如果 fix f返回一个值 - 对于许多输入它不会(尝试fix (+ 1))。即使对于表面上确实具有固定点的函数(例如f x = 2^(x-1)),它也不返回值。但那是另一个故事......