Haskell:修复或不修复

Vad*_*dim 18 haskell fixpoint-combinators

我最近了解到Data.Function.fix,现在我想在任何地方应用它.例如,每当我看到递归函数时,我想" fix"它.所以基本上我的问题是我应该在何时何地使用它.

为了使它更具体:

1)假设我有以下代码用于分解n:

f n = f' n primes
  where
    f' n (p:ps) = ...
    -- if p^2<=n: returns (p,k):f' (n `div` p^k) ps for k = maximum power of p in n
    -- if n<=1: returns []
    -- otherwise: returns [(n,1)]
Run Code Online (Sandbox Code Playgroud)

如果我改写它fix,我会获得一些东西吗?失去什么?有可能,通过重写一个显式的递归到fix-version我会解决,反之亦然会创建一个堆栈溢出?

2)处理列表时,有几种解决方案:递归/修复,foldr/foldl/foldl',可能还有别的东西.关于何时使用每种方法,是否有任何一般指导/建议?例如,您是否会使用foldr无限的素数列表重写上面的代码?

可能还有其他重要问题没有在这里讨论.欢迎任何与使用相关的其他评论fix.

J. *_*son 19

通过以明确的fix形式书写可以获得的一点是递归是"开放的".

factOpen :: (Integer -> Integer) -> Integer -> Integer
factOpen recur 0 = 1
factOpen recur n = n * recur (pred n)
Run Code Online (Sandbox Code Playgroud)

我们可以fix用来定期fact回来

fact :: Integer -> Integer
fact = fix factOpen
Run Code Online (Sandbox Code Playgroud)

这有效,因为fix有效地将函数本身作为其第一个参数传递.但是,通过使递归保持打开状态,我们可以修改哪个函数被"传回".使用该属性的最好的例子是使用像memoFixmemoize.

factM :: Integer -> Integer
factM = memoFix factOpen
Run Code Online (Sandbox Code Playgroud)

现在factM已经内置了memoization.

实际上,我们有开放式递归要求我们将递归位作为一阶事物.递归绑定是Haskell允许在语言级别递归的一种方式,但我们可以构建其他更专业的表单.


use*_*038 7

我想提一下另一种用法fix; 假设您有一个由加法,负数和整数文字组成的简单语言.也许你已经写了一个解析器,它接受String并输出一个Tree:

data Tree = Leaf String | Node String [Tree]
parse :: String -> Tree

-- parse "-(1+2)" == Node "Neg" [Node "Add" [Node "Lit" [Leaf "1"], Node "Lit" [Leaf "2"]]]
Run Code Online (Sandbox Code Playgroud)

现在,您想要将树评估为单个整数:

fromTree (Node "Lit" [Leaf n]) = case reads n of {[(x,"")] -> Just x; _ -> Nothing}
fromTree (Node "Neg" [e])      = liftM negate (fromTree e) 
fromTree (Node "Add" [e1,e2])  = liftM2 (+) (fromTree e1) (fromTree e2)
Run Code Online (Sandbox Code Playgroud)

假设有人决定扩展语言; 他们想要增加乘法.他们必须能够访问原始源代码.他们可以尝试以下方法:

fromTree' (Node "Mul" [e1, e2]) = ...
fromTree' e                     = fromTree e
Run Code Online (Sandbox Code Playgroud)

但那时Mul只能出现一次,在表达式的顶层,因为调用fromTree不会意识到这种Node "Mul"情况.Tree "Neg" [Tree "Mul" a b]不起作用,因为原来fromTree没有图案"Mul".但是,如果使用fix以下函数编写相同的函数:

fromTreeExt :: (Tree -> Maybe Int) -> (Tree -> Maybe Int)
fromTreeExt self (Node "Neg" [e]) = liftM negate (self e)
fromTreeExt .... -- other cases

fromTree = fix fromTreeExt
Run Code Online (Sandbox Code Playgroud)

然后扩展语言是可能的:

fromTreeExt' self (Node "Mul" [e1, e2]) = ...
fromTreeExt' self e                     = fromTreeExt self e

fromTree' = fix fromTreeExt'
Run Code Online (Sandbox Code Playgroud)

现在,扩展fromTree'将正确评估树,因为selfin fromTreeExt'指的是整个函数,包括"Mul"情况.

这里使用这种方法(上面的例子是本文中使用的一个紧密改编的版本).