【发布时间】:2012-04-11 01:32:17
【问题描述】:
所有示例均取自 SICP Book: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/
在scheme中,你可以把'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)
2012 年 3 月 8 日更新
感谢您的回答!
本质上,'letfn' 与 'let' 并没有太大的不同——被调用的函数必须嵌套在 'letfn' 定义中(与 Scheme 不同,Scheme 中的函数在其定义之后的下一个 sexp 中使用并且仅存在在定义它的顶级函数的范围内)。
所以另一个问题......为什么clojure不提供执行方案的能力?这是某种语言设计决定吗?我喜欢计划组织的地方是:
1) 想法的封装,这样我作为程序员就知道哪些小块正在被使用更大的块 - 特别是如果我只在大块中使用小块一次(无论出于何种原因,即使小块本身很有用)。
2) 这也停止了使用对最终用户无用的小程序来污染命名空间(我编写了 clojure 程序,一周后回到他们那里,不得不重新学习我的代码,因为它是一个扁平的结构,我觉得我是从里到外查看代码,而不是以自上而下的方式)。
3) 一个通用的方法定义接口,这样我就可以拉出一个特定的子方法,取消缩进对其进行测试,然后将更改后的版本粘贴回去,而无需过多摆弄。
为什么这不是在 clojure 中实现的?
【问题讨论】:
-
这是一个有趣的问题,但我不认为我会尝试将所有这些都放在一个函数中。