2013-08-24 4 views
6

Можно ли определить две функции в clojure, которые рекурсивно называют друг друга? Например, эта пара:Две функции, которые называют друг друга рекурсивно

(defn a [x] 
    (if (= 0 x) 0 (b (dec x)))) 

(defn b [x] 
    (if (= 0 x) 0 (a (dec x)))) 

Компиляция завершается с:

Unable to resolve symbol: b in this context 

Поскольку я не определен b, когда я пытаюсь вызвать его в a.

например, рубина это работает отлично:

def a(x) 
    x == 0 ? x : b(x-1) 
end 

def b(x) 
    x == 0 ? x : a(x-1) 
end 

ответ

6

либо:

(declare b) ...; Остальная часть кода может быть использован как

или:

(def mutual 
(letfn [(a [ ... ] ...) 
     (b [ ... ] ...)] 
    [a b])) 

(def a (first mutual)) 
(def b (second mutual)) 
+0

Понятно, что есть компромисс для скорости компиляции, который он не может смотреть вперед для определения функций. Благодаря удаленному ответу для этой ссылки на документы (http://clojuredocs.org/clojure_core/clojure.core/declare) – spike

+0

Компромисс не для скорости компиляции, а для обнаружения ошибок. Существует также функция 'resolve', когда два пространства имен должны взаимно ссылаться друг на друга. – noisesmith

+0

У вас есть ссылка для получения дополнительной информации об этом? Какие ошибки избегают компилятор, игнорируя явные определения функций? – spike

6

В зависимости от выполнения кода, имейте в виду, что вы можете получить исключение переполнения стека.

Существует функция (clojure.core/trampoline), которая входит в игру и выполняет свою магию.

батут может быть использован для преобразования алгоритмов, требующих взаимного использования. рекурсия без использования стека. Вызывает f с указанными аргументами, если любой. Если f возвращает fn, вызывает fn без аргументов, а продолжает повторять, пока возвращаемое значение не будет fn, тогда возвращает это значение non-fn. Обратите внимание, что если вы хотите вернуть fn в качестве окончательного значения , вы должны перенести его в некоторую структуру данных и распаковать его после возвращения батута.

+0

ha! первое сообщение в блоге, которое я нашел об этом, даже использовал мои примерные функции: http://pramode.net/clojure/2010/05/08/clojure-trampoline/ – spike

Смежные вопросы