Как ускорить (или запомнить) ряд взаимно рекурсивных функций
У меня есть программа, которая производит ряд функций f
а также g
который выглядит следующим образом:
step (f,g) = (newF f g, newG f g)
newF f g x = r (f x) (g x)
newG f g x = s (f x) (g x)
foo = iterate step (f0,g0)
Где r и s - некоторые неинтересные функции f x
а также g x
, Я наивно надеялся, что имея foo
быть списком будет означать, что, когда я называю N f
он не будет пересчитывать (n-1) -й f
если он уже вычислил его (как это случилось бы, если f
а также g
не были функции). Есть ли способ запомнить это, не разрывая всю программу на части (например, оценивая f0
а также g0
на все уместные аргументы, а потом работает наверх)?
2 ответа
Может оказаться полезным Data.MemoCombinators (в пакете data-memocombinators).
Вы не говорите, какой аргумент вводит ваш f
а также g
взять --- если они оба принимают целочисленные значения, то вы бы использовали это так:
import qualified Data.MemoCombinators as Memo
foo = iterate step (Memo.integral f0, Memo.integral g0)
При необходимости вы также можете запомнить результат каждого шага
step (f,g) = (Memo.integral (newF f g), Memo.integral (newG f g))
Я надеюсь, что вы не видите, что это разрывает всю программу на части.
В ответ на ваш комментарий:
Это лучшее, что я могу придумать. Это не проверено, но должно работать в правильном направлении.
Я беспокоюсь, что преобразование между Double
а также Rational
излишне неэффективно --- если бы был Bits
экземпляр для Double
мы могли бы использовать Memo.bits
вместо. Так что это может в конечном итоге не иметь никакого практического применения для вас.
import Control.Arrow ((&&&))
import Data.Ratio (numerator, denominator, (%))
memoV :: Memo.Memo a -> Memo.Memo (V a)
memoV m f = \(V x y z) -> table x y z
where g x y z = f (V x y z)
table = Memo.memo3 m m m g
memoRealFrac :: RealFrac a => Memo.Memo a
memoRealFrac f = Memo.wrap (fromRational . uncurry (%))
((numerator &&& denominator) . toRational)
Memo.integral
Другой подход.
У тебя есть
step :: (V Double -> V Double, V Double -> V Double)
-> (V Double -> V Double, V Double -> V Double)
Как насчет того, чтобы изменить это на
step :: (V Double -> (V Double, V Double))
-> (V Double -> (V Double, V Double))
step h x = (r fx gx, s fx gx)
where (fx, gx) = h x
А также изменить
foo = (fst . bar, snd . bar)
where bar = iterate step (f0 &&& g0)
Надеюсь, что общий fx
а также gx
должно привести к небольшому ускорению.
Есть ли способ запомнить это, не разрывая всю программу на части (например, вычисляя f0 и g0 по всем соответствующим аргументам и затем работая вверх)?
Это может быть то, что вы подразумеваете под "разрывом всей программы", но вот решение, в котором (я считаю, но не могу проверить банкомат) fooX
можно поделиться.
nthFooOnX :: Integer -> Int -> (Integer, Integer)
nthFooOnX x =
let fooX = iterate step' (f0 x, g0 x)
in \n-> fooX !! n
step' (fx,gx) = (r fx gx, s fx gx)
-- testing definitions:
r = (+)
s = (*)
f0 = (+1)
g0 = (+1)
Я не знаю, сохраняет ли это дух вашей первоначальной реализации.