Можно ли в Clojure совместить мемоизацию и оптимизацию хвостовых вызовов?

В clojure я хотел бы написать хвостовую рекурсивную функцию, которая запоминает свои промежуточныерезультаты для последующих звонки.

[EDIT: этот вопрос был переписан с использованием gcdв качестве примера вместо factorial.]

Запомненный gcd(наибольший общий делитель) может быть реализовано следующим образом:

(def gcd (memoize (fn [a b] 
   (if (zero? b) 
       a 
       (recur b (mod a b)))) 

В этой реализации, промежуточныерезультаты не запоминаются для последующихвызовов. Например, для вычисления gcd(9,6)в качестве промежуточного результата вызывается gcd(6,3). Однако gcd(6,3)не сохраняется в кэше запоминаемой функции, потому что точка рекурсии recurявляется анонимной функцией, которая не запоминается.

Следовательно, если после вызова gcd(9,6)мы вызовем gcd(6,3)мы не выиграем от мемоизации.

Единственным решением, о котором я могу думать, будет использование мирской рекурсии (явный вызов gcdвместо recur), но тогда мы не выиграем от оптимизации хвостового вызова.

Bottom Line

Есть ли способ добиться того и другого:

  1. Оптимизация хвостового вызова
  2. Запоминание промежуточных результатов для последующих вызовов

Замечания

  1. Этот вопрос аналогичен Комбинировать запоминание и хвостовая рекурсия. Но все ответы там относятся к F#. Здесь я ищу ответ в clojure.
  2. Этот вопрос был оставлен читателю в качестве упражнения The Joy of Clojure(глава 12.4). Вы можете обратиться к соответствующей странице книги по адресу http://bit.ly/HkQrio.

14
задан Community 23 May 2017 в 11:48
поделиться