Ошибка "Возврат только из хвостовой позиции, компиляция" при использовании повторения в цикле
Я очень новичок в Clojure, и на практике я пытаюсь применить простой алгоритм для полуслучайных чисел.
Несколько дней назад я читал о циклах в документах clojure и о том, как они работают с использованием recur
я попытался написать цикл с такими строками кода:
(def numbers_semi_random
(fn []
(loop[Xn 4 count 0]
(while (and (not (= Xn m)) (< count m))
(println (mod (+ (* 5.0 Xn) 7.0) m))
(recur (mod (+ (* 5.0 Xn) 7.0) m) (inc count))
))))
Но когда я выполняю код, эта ошибка отображается
CompilerException java.lang.UnsupportedOperationException: Can only recur from tail position, compiling
Что происходит? recur
не в хвосте функции?
3 ответа
Положение хвоста является последней формой, которую необходимо оценить перед тем, как покинуть область. Когда используешь loop
, recur
оператор должен быть в состоянии начать заново с новыми привязками, дальнейших вычислений из предыдущего цикла не требуется.
В вашем коде у вас есть while
блок в хвостовой части loop
, Место, куда вы пытаетесь позвонить recur
не находится в положении хвоста петли (хотя это находится в хвосте его хвоста).
Возможно, вы предназначены для recur
призыв быть вне while
? Но сравнение привязок петли Xn
а также count
к глобальной переменной m
в тесте на while
заставляет меня думать, что есть еще что-то, что можно распутать здесь. Сколько вложенных циклов вы хотите здесь?
Вполне вероятно, что ваше намерение будет достигнуто с помощью when
на месте while
(ваш цикл будет продолжать печатать обновленные значения Xn
пока условие не выполнено), но для объяснения исключения:
С точки зрения компилятора, проблема в том, что в то время как while
форма действительно в хвостовой позиции по отношению к вашему loop
, while
расширение макроса таково, что ничего в while
Тело находится в хвостовой позиции по отношению к while
форма. (Было ли тело в хвостовой позиции по отношению к while
форма, она также будет в хвостовом положении с ограждающими loop
.)
Причина этого в том, что while
расширяется до loop
выражение и поставляет свои собственные recur
после предоставленного пользователем тела:
(while test
body...)
;; expands to
(loop []
(when test
body...
(recur)))
Итак, если вы поставите recur
как часть тела while
что вы получаете
(loop []
(when test
...
(recur ...) ; from user code
(recur)))
Здесь пользователь предоставил (recur ...)
не находится в положении хвоста относительно его немедленно вмещающего loop
и это то, на что жалуется компилятор.
Ваша проблема заключалась в том, что вы использовали "время" вместо "когда". Хотя обычно используется с "do", если вы хотите испускать побочные эффекты
(def n (atom -33)
(while (neg? @n) (do (... something with side effect)) (swap! n inc))
Правильная реализация вашего кода будет...
(def numbers_semi_random
(fn [m]
(loop[Xn 4 count 0]
(when (and (not (= Xn m)) (< count m))
(println (mod (+ (* 5.0 Xn) 7.0) m))
(recur (mod (+ (* 5.0 Xn) 7.0) m) (inc count))))))
Придерживайтесь 'когда' или 'если' при использовании рекурсии в Clojure, это намного проще.