2010-07-10 54 views
5

我有此功能(产生Fibonacci序列):如何将这个Haskell表达式分解以避免重复计算?

unfoldr (\(p1, p2) -> Just (p1+p2, (p1+p2, p1))) (0, 1) 

在这里,我注意到重复表达式,p1+p2,我想因数,以使得它仅计算一次。除了本身不是一个昂贵的计算,但对于一个更一般的版本:

unfoldr (\(p1, p2) -> Just (f p1 p2, (f p1 p2, p1))) (0, 1) 
    where f = arbitrary, possibly time-consuming function 

在上述情况下,f p1 p2计算两次(除非有一些神奇的编译器的优化,我不知道),这可能会造成性能瓶颈如果f需要大量的计算。我不能将f p1 p2分解为where,因为p1p2不在范围内。将这个表达式分解为f只计算一次的最佳方式是什么?

回答

9
unfoldr (\(p1, p2) -> let x = f p1 p2 in Just (x, (x, p1))) (0, 1) 
    where f = arbitrary, possibly time-consuming function 
+0

谢谢!感谢您花时间学习像这样的初学者问题(: – guhou 2010-07-10 13:34:43

2

Control.Arrow(&&&)可以在一些像这样使用:

unfoldr (\(p1,p2) -> (Just . (id &&& flip (,) p1)) (p1+p2)) (0,1) 

甚至:

unfoldr (Just . (fst &&& id) . (uncurry (+) &&& fst)) (0,1) 

除了在你的例子p1+p2实际上是未来p1所以你可以重写它像

tail (unfoldr (\(p1, p2) -> Just (p1, (p1+p2, p1))) (0, 1))