Циклические зависимости функции clojure специально запрещены проектом, или это просто поведение читателя?

Если я сделаю следующее в clojure

(defn sub1a [a]
  (cond
    (= a 0) 0
    true (sub1b (- a 1) )))

(defn sub1b [a]
  (cond
    (= a 0) 0
    true (sub1a (- a 1) )))

(println (sub1a 10))

Я получаю следующую ошибку:

java.lang.Exception: Unable to resolve symbol: sub1b in this context

Но если я сделаю следующее:

(defn sub1a [a]
  (cond
    (= a 0) 0
    true (- a 1)))

(defn sub1b [a]
  (cond
    (= a 0) 0
    true (- a 1)))

(defn sub1a [a]
  (cond
    (= a 0) 0
    true (sub1b (- a 1) )))

(defn sub1b [a]
  (cond
    (= a 0) 0
    true (sub1a (- a 1) )))

(println (sub1a 10))

Работает просто отлично.

Это от замысла или просто от того, как работает ридер Clojure?

2 ответа

Решение

Ты можешь сделать

(declare sub1a sub1b)

"объявлять" специально предназначено для создания переменной без привязок для отправки предварительных объявлений.

Тот, который вы объявили имена:

(defn sub1a [a]
  (cond
    (= a 0) 0
    true (sub1b (- a 1) )))

(defn sub1b [a]
  (cond
    (= a 0) 0
    true (sub1a (- a 1) )))

(println (sub1a 10))

Также идиоматическим способом указания условия по умолчанию в cond (для clojure) является использование:else. Это немного отличается от Common Lisp, который использует T (для True). Таким образом, ваш предыдущий код может быть переписан как:

(defn sub1a [a]
  (cond
    (= a 0) 0
    :else (sub1b (- a 1) )))

...

Правильное решение, как опубликовано rkrishnan.

Что касается этой части вопроса:

Это от замысла или просто от того, как работает ридер Clojure?

На самом деле это не имеет ничего общего с читателем Clojure - это потому, что компилятор разрешает символы в Vars сразу же после их обнаружения (в тех местах, где они должны были бы "в конечном итоге" быть разрешены в Var, в отличие от мест, где они называют локальные, цитируются или передаются в специальную форму или макрос, конечно). Это имеет смысл по соображениям эффективности: знание того, к какому Var относится символ во время компиляции, позволяет генерировать код, который не должен разрешать символы во время выполнения (обычно он все же должен искать значения Vars, но не сами Vars). Если вы действительно хотите, вы можете сделать так, чтобы ваш код разрешал символы во время выполнения:

(defn sub1a [a]
  (cond
    (= a 0) 0
    :else ((resolve 'sub1b) (- a 1) )))

(defn sub1b [a]
  (cond
    (= a 0) 0
    :else ((resolve 'sub1a) (- a 1) )))

(println (sub1a 10))

; prints 0 and returns nil

Это, однако, вызывает некоторое снижение производительности, которое вряд ли когда-либо оправдано в реальном коде, поэтому Clojure заставляет вас быть откровенным, если вы действительно думаете, что это то, что вы хотите.

Другие вопросы по тегам