Чтобы установить некоторый контекст, я в процессе обучения Clojure и Lisp развивается более широко. На моем пути к Lisp я в настоящее время работаю в серии "Маленькие", чтобы укрепить фундамент в функциональном программировании и рекурсивном решении. В "The Little Schemer", я работал над многими упражнениями, однако, я немного пытаюсь преобразовать некоторые из них в Clojure. В частности, я изо всех сил пытаюсь преобразовать их, чтобы использовать "recur", чтобы включить TCO. Например, здесь реализована реализация на основе Clojure функции "происходит" (от Little Schemer), которая подсчитывает количество вхождений атома, входящих в список S-выражений:
(defn atom? [l]
(not (list? l)))
(defn occurs [a lst]
(cond
(empty? lst) 0
(atom? (first lst))
(cond
(= a (first lst)) (inc (occurs a (rest lst)))
true (occurs a (rest lst)))
true (+ (occurs a (first lst))
(occurs a (rest lst)))))
В принципе, (occurs 'abc '(abc (def abc) (abc (abc def) (def (((((abc)))))))))
будет оцениваться до 5. Очевидная проблема заключается в том, что это определение потребляет стековые фреймы и выдувает стек, если слишком сильно указывается список S-выражений.
Теперь я понимаю вариант рефакторинга рекурсивных функций, чтобы использовать параметр аккумулятора, чтобы включить рекурсивный вызов в положение хвоста (чтобы разрешить TCO), но я боюсь, если этот параметр даже применим к ситуациям, таким как этот.
Здесь, как далеко я получу, если попытаюсь реорганизовать это, используя "recur" вместе с использованием параметра аккумулятора:
(defn recur-occurs [a lst]
(letfn [(myoccurs [a lst count]
(cond
(empty? lst) 0
(atom? (first lst))
(cond
(= a (first lst)) (recur a (rest lst) (inc count))
true (recur a (rest lst) count))
true (+ (recur a (first lst) count)
(recur a (rest lst) count))))]
(myoccurs a lst 0)))
Итак, я чувствую, что я почти там, но не совсем. Очевидной проблемой является мое предложение "else", в котором глава списка не является атомом. Понятно, что я хочу суммировать результат повторения по первому элементу списка с результатом повторения по остальной части списка. Я изо всех сил пытаюсь реорганизовать это так, что рекурсия может быть перенесена в положение хвоста.
Существуют ли дополнительные методы для шаблона "аккумулятора" для достижения ваших рекурсивных вызовов, помещенных в позицию хвоста, которые я должен применять здесь, или это проблема просто более "фундаментальная" и что нет чистого Clojure на основе решения JVM отсутствия ТСО? Если последнее, вообще говоря, какова должна быть общая модель для программ Clojure для использования, которые должны повторяться в списке S-выражений? Для того, что это стоит, я видел метод multi method w/lazy-seq, используемый (ссылка на "Halloway" Programming Clojure "для справки), чтобы" заменить рекурсию на лень ", но я не уверен, как применять этот шаблон к этому примеру, в котором я не пытаюсь создать список, но для вычисления целочисленного значения.
Заранее благодарю вас за любые рекомендации по этому вопросу.