Как я могу учесть это выражение на Haskell, чтобы избежать повторных вычислений? - PullRequest
5 голосов
/ 10 июля 2010

У меня есть эта функция (создает последовательность Фибоначчи):

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, потому что p1 и p2 не входят в сферу применения.Каков наилучший способ вычислить это выражение так, чтобы f вычислялось только один раз?

Ответы [ 2 ]

9 голосов
/ 10 июля 2010
unfoldr (\(p1, p2) -> let x = f p1 p2 in Just (x, (x, p1)) ) (0, 1)
    where f = arbitrary, possibly time-consuming function
2 голосов
/ 10 июля 2010

в 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))
...