Заметка - это способ кеширования результатов в функцию, чтобы избежать перерасчета промежуточных результатов снова и снова. Под мемоизацией в основном подразумевается первый раз, когда вы вызываете функцию с некоторыми аргументами, вычисляете ответ и возвращаете его, и кешируете этот ответ; для последующих вызовов функции с теми же аргументами просто верните кэшированное значение.
В Лиспе вы можете легко использовать функции высшего порядка и макрос для прозрачного запоминания функции. Clojure имеет memoize
в качестве стандартной функции. Также смотрите на странице 65 На Лиспе реализацию Common Lisp memoize
. Вот оно в Clojure:
(defn fib-naive [i]
(if (or (= i 1) (= i 2))
1
(+ (fib-naive (- i 1)) (fib-naive (- i 2)))))
(def fib-memo
(memoize (fn [i]
(if (or (= i 1) (= i 2))
1
(+ (fib-memo (- i 1)) (fib-memo (- i 2)))))))
user> (time (fib-naive 30))
"Elapsed time: 455.857987 msecs"
832040
user> (time (fib-memo 30))
"Elapsed time: 0.415264 msecs"
832040
user>
Это все еще может вызвать переполнение стека, если вы вызываете его с большим целым числом. например немедленное выполнение (fib 10000)
взорвет стек, потому что он все еще должен проходить очень глубоко (один раз). Но если вы сначала заполняете кеш, он больше не должен глубоко зацикливаться, и этого можно избежать. Просто сделаем это сначала (в Clojure):
(dorun (map fib-memo (range 1 10000)))
будет достаточно, чтобы позволить вам без проблем выполнить (fib 10000)
.
(Конкретный вопрос вычисления чисел Фибоначчи недавно появился в списке рассылки Clojure . Там есть решение, основанное на числах Лукаса , которые я не понимаю ни в малейшей степени , но который предположительно в 40 раз быстрее наивного алгоритма.)