Вы должны иметь мысленную модель того, как работает ваша функция.Допустим, вы выполняете свою функцию самостоятельно, используя клочки бумаги для каждого вызова.Сначала записка, вы пишете (fib 250000)
, затем вы видите «о, мне нужно вычислить (fib 249999)
и (fib 249998)
и, наконец, добавить их», так что вы заметили это и начали два новых записки.Вы не можете выбросить первое, потому что у него все еще есть дела, когда вы закончите другие вычисления.Вы можете себе представить, что для этого расчета понадобится много записок.
Другой способ - начать не сверху, а снизу.Как бы вы сделали это вручную?Вы должны начать с первых чисел, 1, 1, 2, 3, 5, 8…, а затем всегда добавлять последние два, пока не сделаете это i
раз.Вы даже можете выбрасывать все числа, кроме последних двух, на каждом шаге, поэтому вы можете повторно использовать большинство записок.
(defn fib [i]
(loop [a 0
b 1
n 1]
(if (>= n i)
b
(recur b
(+ a b)
(inc n)))))
Это также довольно очевидная реализация, но из как , а не что .Это всегда кажется довольно элегантным, когда вы можете просто записать определение, и оно автоматически преобразуется в эффективный расчет, но программирование - это преобразование.Если что-то преобразуется автоматически, то эта конкретная проблема уже решена (часто в более общем смысле).
Размышление «как бы я сделал это шаг за шагом на бумаге» часто приводит к хорошей реализации.