Существует ряд проблем с этим кодом. Чтобы предоставить вам рабочий пример, давайте перепишем функции root
и count_root
:
(defn power [nth result]
(* result (- nth 1)))
(defn count-root [nth guess]
(loop [result guess
last_result guess
temp (power nth result)]
(cond
(> (Math/abs (- result last_result)) 0.01)
(let [next-result (* (/ 1.0 nth) (+ (* (- nth 1) result) (/ guess temp)))]
(recur next-result
result
(power nth next-result)))
:else
(str "Result: " result))))
(defn root [nth guess]
(cond
(<= guess 0) "Root doesn't exists"
:else (count-root nth guess)))
Давайте посмотрим, что здесь изменилось, и попробуем объяснить.
Сначала в функции count-root
:
- Я предлагаю не использовать подчеркивания в именах. Наиболее распространенное соглашение Clojure - использовать тире для разделения элементов имен функций и определений.
- Чтобы выполнить цикл с определениями, используйте конструкцию
loop
, чтобы определить, где начинается цикл, вместе с любыми необходимыми определениями, и используйте recur
, чтобы выполнить рекурсивный вызов цикла с новыми значениями, указанными для определения. Обратите внимание, что если бы все необходимые определения были заданы в качестве параметров функции count-root
, то цикл не потребовался бы, и рекурсия могла бы вернуться к началу функции.
Я рекомендую вам привыкнуть использовать cond
вместо if
. Функция if
выглядит целым лотом как оператор if
на других языках, и это время от времени приводит нас (ну, хорошо, ME :-) к трудностям , Проблема в том, что когда вы используете if
, вы должны помнить, чтобы указать ОБА действия "true" и "false", потому что форма if
не может быть "сокращенной". То есть, если бы это было так на «других» языках, вы всегда должны были бы написать:
ЕСЛИ что-то
ТОГДА правда
ELSE фальшивка
ENDIF
или, более Clojurist моды
(if something
true-stuff
false-stuff)
В Clojure не следует писать
(if something
true-stuff)
потому что я обнаружил, что это не работает правильно. Если вы вызываете функцию if
и задаете only аргумент true
, как вы часто делаете в «других» языках программирования, он будет компилироваться и выполняться, но вы получите странные результаты, вещи этого не может произойти, и, как правило, вы запутаетесь, пока не поймете, что у вас есть if
без «ложной» половины или чего-то в этом роде. Избавьте себя от неприятностей - используйте cond
вместо if
.
4. Я предлагаю использовать Math/abs
вместо определения собственной функции absolute
.
5. Используйте форму let
, чтобы определить временные значения, когда вам не нужно будет зацикливаться внутри формы. Здесь я определил next-result
как временное значение, которое я буду использовать в следующей форме.
6. Используйте recur
для рекурсивного «прыжка» в хвост к следующей самой ранней точке рекурсии. Здесь следующая самая ранняя точка рекурсии - форма loop
. Если бы у нас не было этой формы loop
или какой-либо другой точки рекурсии, определение функции для count-root
послужило бы точкой рекурсии. Когда
Далее в функции root
:
- определение функции было неверным в исходной версии. Я убрал это здесь так, что функция определена с двумя аргументами,
nth
и guess
.
EDIT
В оригинальной версии count_root
вы использовали функцию while
. Использование while
требует, чтобы его аргумент изменился, что означает, что ему придется так или иначе иметь дело с атомом; то есть он имеет дело с изменяемой переменной. Большую часть времени в Clojure то, что обычно считается «переменными», на самом деле не может быть изменено. Это намеренно. Clojure с самого начала предназначался для написания многопоточных приложений, где координация изменений в «переменных» становится серьезной проблемой. Если «переменные» на самом деле не могут быть изменены, необходимость в такой координации (посредством таких конструкций, как семафоры, критические секции и т. Д.) Исчезает. Таким образом, определения значений (они на самом деле не являются «переменными», если они не могут изменяться или изменяться) по умолчанию постоянны. Теперь любая «настоящая» программа нуждается в значениях, которые меняются со временем, и Clojure обеспечивает такие потребности, но это не значение по умолчанию. Вы должны четко подумать о том, что нужно изменить, и вам нужно по-разному обращаться к этим изменяемым значениям.
Теперь наблюдение за вами. Вы можете обнаружить, что программирование на Clojure или на любом языке, который заставляет вас делать вещи не так, как вы привыкли, поначалу будет казаться очень странным и, возможно, даже неудобно трудным. Я знаю, что это то, через что я прошел, изучая новые для меня системы. Довольно часто наши мыслительные процессы попадают в «колеи», и все, что заставляет нас выходить из этих «колей», делает нас некомфортными. Но иногда пробиваясь через этот «неудобный» период, вы получаете что-то ценное. Если вы будете придерживаться Clojure, вы научитесь думать о программном обеспечении по-другому - и иметь несколько точек зрения, с которых можно рассматривать проблему, само по себе хорошо.
Когда вы изучаете что-то новое, полезно хорошее руководство. Я предлагаю Clojure For The Brave And True , преимущество которого заключается в том, что его онлайн-издание бесплатное, как пиво.
Удачи.