Прежде всего, вы можете сопоставить с шаблоном в привязке верхнего уровня. Обычно это не означает, что происходит много интересного, но если вы хотите поделиться локальными помощниками между двумя привязками верхнего уровня, это может помочь.
m2 :: Int -> Int
f2 :: Int -> Int
(m2, f2) = (gom, gof)
where
gof 0 = 1
gof i = i - ms !! ( fs !! (i-1) )
gom 0 = 0
gom i = i - fs !! ( ms !! (i-1) )
fs = map gof [0..]
ms = map gom [0..]
Вы заметите, что там есть еще один трюк. Вместо того, чтобы ограничивать списки fs
и ms
их максимальными размерами, я просто позволяю лениться справляться с их ограничением. Списки не будут созданы после того, как они были необходимы для запоминания более ранних результатов.
Но индексирование списка - O (n). Избавление даже от некоторых из них может быть значительным ускорением. Если вы посмотрите на шаблон рекурсии для одной и той же функции, вы увидите, что gom i
всегда вызывает gom (i-1)
, и то же самое с gof
. Вы можете использовать это для удаления индексации списка в этих поисках, просто передав предыдущее значение. К сожалению, то же самое не относится к вызовам противоположной функции, поскольку они не так легко следуют. Но это все еще убирает большой объем работы. И это можно сделать таким образом, чтобы еще больше использовать лень:
m3, f3 :: Int -> Int
(m3, f3) = ((ms !!), (fs !!))
where
(ms, fs) = unzip pairs
pairs = (0, 1) : zipWith iter [1..] pairs
iter i (mp, fp) = (i - fs !! mp, i - ms !! fp)
Рекурсивные вспомогательные функции были заменены одновременным отложенным созданием обоих списков результатов. Этот шаблон отличается от стандартной рекурсии тем, что для него не требуется базовый случай, и ему требуется некоторая защита от попыток немедленно найти базовый случай, прежде чем будет предоставлен полный ответ. Эта модель известна как ко-рекурсия. (Или corecursion, если я набираю лениво.) Та же идея, но она дает ответ в противоположном направлении.