用于简化递归的惯用Haskell代码

时间:2013-03-26 11:34:46

标签: haskell recursion idiomatic

我需要计算foo n = maximumBy (comparing p) [1..n],其中p :: Int -> Int很慢。但我知道p n < n适用于所有n > 0,并希望通过以下方式使用此事实来加速此计算:我为p x计算x,以{{1}开头向下n,记住当前的最大值。一旦我达到1小于或等于当前最大值,我知道这个最大值必须是全局的,我就完成了。

所以我的尝试看起来像这样:

x

这有效,但看起来并不是很惯用。所以我正在寻找更优雅的解决方案。你有什么建议吗?

2 个答案:

答案 0 :(得分:6)

您可以使用模式匹配来减少if ... then ... else的使用 另一个技巧是给你的变量一个数字,它允许你记住起始情况 var0 ,对于另一个递归调用,你可以使用更好的 var
最后请注意,您有一些 if 在相同表单的谓词之后返回相同的值并共享相同的环境,那么您可以将它们组合在一起。

foo n0 = go (0, 0) n0
  where
    go (x, y) n
        | (n  == 1) || (y >= n) = x
        | y < (p n) = go (n, (p n)) (n-1)
        | otherwise = go (x, y) (n-1)

重写考虑评论,

foo n0 = go 0 0 n0
  where
    go x y n
        | (n  == 1) || (y >= n) = x
        | pn > y                = go n pn (n-1)
        | otherwise             = go x y (n-1)
          where
            pn = p n

答案 1 :(得分:4)

好的,那么让我看看我是否正确地围绕着这个......你说的是p n < n所有有趣的n。并且您想为p x计算x = n to 1,直到x变得小于目前为止看到的最大p x

好吧,看起来您可以将所有p x计算为惰性列表。现在问题是减少扫描此列表,直到找到您要查找的内容。我建议takeWhile,除了我们还需要折叠列表以找到当前最大值。嗯,也许我们可以将每个值与运行的最大值配对?

这样的东西
foo n =
  let
    ps = [ p x | x <- [n, n-1 .. 1] ]
    qs = fold (\ px' (px, maxPX) -> (px', maxPX `max` px') ) ps
  in last . takeWhile (\ (px, maxPX) -> px >= maxPX) qs

或类似的?