Все примеры взяты из Книги SICP: http://sicpinclojure.com/?q=sicp/1-3-3-procedures-general-methods
Это было мотивировано из серии видеороликов MIT на LISP - http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/2a-higher-order-procedures/
В схеме вы можете поместить 'define' внутри другого 'define':
(define (close-enough? v1 v2)
(define tolerance 0.00001)
(< (abs (- v1 v2)) tolerance ) )
В clojure есть оператор let, с той лишь разницей, что он вложен:
(defn close-enough? [v1 v2]
(let [tolerance 0.00001]
(< (Math/abs (- v1 v2) )
tolerance) ) )
Но как насчет переписать в clojure нечто большее, как это?:
(define (sqrt x)
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(define tolerance 0.00001)
(< (abs (- v1 v2)) tolerance))
(define (try guess)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next))))
(try first-guess))
(fixed-point (lambda (y) (average y (/ x y)))
1.0))
Это на самом деле работает, но выглядит очень нетрадиционно ...
(defn sqrt [n]
(let [precision 10e-6
abs #(if (< % 0) (- %) %)
close-enough? #(-> (- %1 %2) abs (< precision))
averaged-func #(/ (+ (/ n %) %) 2)
fixed-point (fn [f start]
(loop [old start
new (f start)]
(if (close-enough? old new)
new
(recur new (f new) ) ) ) )]
(fixed-point averaged-func 1) ) )
(sqrt 10)
ОБНОВЛЕНО март / 8/2012
Спасибо за ответ!
По сути, 'letfn' не слишком отличается от 'let' - вызываемые функции должны быть вложены в определение 'letfn' (в отличие от Scheme, где функции используются в следующем sexp после его определений и только существуют в рамках функции верхнего уровня, в которой она определена).
Итак, еще один вопрос ... Почему clojure не дает возможности делать то, что делает схема? Это какое-то решение по дизайну языка? Что мне нравится в схеме организации:
1) Инкапсуляция идей, так что я, как программист, имею представление о том, какие маленькие блоки используются с большим блоком - особенно если я использую маленькие блоки только один раз в большом блоке (по любой причине , даже если маленькие блоки полезны сами по себе).
2) Это также останавливает загрязнение пространства имен небольшими процедурами, которые бесполезны для конечного пользователя (я написал программы clojure, вернулся к ним через неделю и мне пришлось заново изучать мой код, потому что он был в плоской структуре, и я чувствовал, что смотрю на код наизнанку, а не сверху вниз).
3) Общий интерфейс определения метода, так что я могу вытащить определенный подметод, отменить его тестирование и вставить измененную версию обратно без особых проблем.
Почему это не реализовано в clojure?