У меня есть эта функция (производит последовательность fibonacci):
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
в a where
потому что p1
и p2
не находятся в объеме. Что лучший способ состоит в том, чтобы учесть это выражение так, чтобы f
только вычисляется однажды?
unfoldr (\(p1, p2) -> let x = f p1 p2 in Just (x, (x, p1)) ) (0, 1)
where f = arbitrary, possibly time-consuming function
в 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))