我需要计算foo n = maximumBy (comparing p) [1..n]
,其中p :: Int -> Int
很慢。但我知道p n < n
适用于所有n > 0
,并希望通过以下方式使用此事实来加速此计算:我为p x
计算x
,以{{1}开头向下n
,记住当前的最大值。一旦我达到1
小于或等于当前最大值,我知道这个最大值必须是全局的,我就完成了。
所以我的尝试看起来像这样:
x
这有效,但看起来并不是很惯用。所以我正在寻找更优雅的解决方案。你有什么建议吗?
答案 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
或类似的?