clojure编写嵌套定义语句(如scheme)的标准方法是什么?

clojure编写嵌套定义语句(如scheme)的标准方法是什么?,clojure,scheme,idioms,Clojure,Scheme,Idioms,所有示例摘自SICP手册: 这源于麻省理工学院的LISP- 在scheme中,您可以将“define”放在另一个“define”中: (define (close-enough? v1 v2) (define tolerance 0.00001) (< (abs (- v1 v2)) tolerance ) ) (定义(足够近?v1 v2) (定义公差0.00001) (

所有示例摘自SICP手册:

这源于麻省理工学院的LISP-

在scheme中,您可以将“define”放在另一个“define”中:

(define (close-enough? v1 v2)
    (define tolerance 0.00001)
    (< (abs (- v1 v2)) tolerance ) )
(定义(足够近?v1 v2)
(定义公差0.00001)
(<(abs(-v1 v2))公差)
在clojure中,有一个“let”语句,唯一的区别是它是嵌套的:

(defn close-enough? [v1 v2]
    (let [tolerance 0.00001]
         (< (Math/abs (- v1 v2) ) 
            tolerance) ) )
(定义是否足够接近?[v1 v2]
(让[公差0.00001]
(<(数学/abs(-v1 v2))
公差()
但是用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))
(定义(sqrt x)
(定义(定点f首次猜测)
(定义(足够近?v1 v2)
(定义公差0.00001)
(<(abs(-v1 v2))公差)
(定义(尝试猜测)
(让((下一个(f猜测)))
(如果(足够近?猜测下一步)
下一个
(试试下一步)
(先猜猜看)
(定点(λ(y)(平均y(/xy)))
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)
(定义sqrt[n]
(让[精度10e-6
abs#(如果(<%0)(%)%)
足够近?#(>(%1%2)abs(<精度))
平均func#(/(+(/n%)%)2)
定点(fn[f开始]
(循环[旧的开始]
新的(f)]
(如果(足够近?新旧)
新的
(重新出现新的(f新的)))]
(定点平均func(1)))
(sqrt 10)
于2012年3月8日更新

谢谢你的回答

本质上,“letfn”与“let”没有太大区别——被调用的函数必须嵌套在“letfn”定义中(与方案相反,在方案中,函数在定义后的下一个sexp中使用,并且仅存在于定义它的顶级函数的范围内)

所以另一个问题。。。为什么clojure不提供scheme所做的功能?这是某种语言设计决策吗?我喜欢该计划的组织结构是:

  • 1) 对想法进行封装,这样作为程序员,我就知道哪些小块在大块中被使用——特别是如果我在大块中只使用一次小块(无论出于何种原因,即使小块本身是有用的)

  • 2) 这也停止了用对最终用户不有用的小程序污染名称空间(我已经编写了clojure程序,一周后回到它们那里,不得不重新学习我的代码,因为它是在一个平面结构中,我觉得我是从内到外看代码,而不是从上到下看代码)

  • 3) 一个通用的方法定义接口,这样我就可以拉出一个特定的子方法,对它进行缩进测试,并将修改后的版本粘贴回去,而不必太费劲


为什么这不在clojure中实现?

在clojure中编写嵌套的命名过程的标准方法是使用
letfn


顺便说一句,您使用嵌套函数的示例非常可疑。示例中的所有函数都可能是顶级非本地函数,因为它们本身或多或少都很有用,并且彼此之间没有任何联系。

批评他在所有一个函数中的位置的人,不理解为什么会这样做。SICP是示例的来源,它试图说明模块的概念,但没有向基础语言添加任何其他构造。所以“sqrt”是一个模块,它的接口中有一个函数,其余的是该模块中的本地或私有函数。我相信这是基于R5RS方案,后来的方案添加了一个标准的模块结构(?)。但不管怎样,它更多地展示了隐藏实现的原理

经验丰富的schemer也会使用类似的嵌套局部函数示例,但通常都会隐藏实现并关闭值


但即使这不是一个教学示例,您也可以看到这是一个非常轻量级的模块,我可能会在一个更大的“真实”模块中这样写。如果计划重用,那么重用是可以的。否则,您只会公开可能不适合您以后需要的功能,同时,给这些功能增加意外用例的负担,这些用例可能会在以后破坏它们。

letfn
是标准方法

但是由于Clojure是一个Lisp,您可以(几乎)创建任何您想要的语义。这里有一个概念证明,它根据
letfn
定义
define

(defmacro define[&form]
(letfn[(定义?[exp]
(和(list?exp)(=(first exp)'define)))
(转换定义[[name args&exps]]
`(~name~args)
(letfn[~@(映射变换定义(过滤器定义?表达式))]
~@(过滤器#(非(定义%))exps))]
`(defn~@(transform define`(define~@form‘‘)'))
(定义sqrt[x]
(定义平均值[ab](/(+ab)2))
(定义定点[f第一猜测]
(定义足够近?[v1 v2]
(让[公差0.00001]
(<(数学/abs(-v1 v2))公差)
(定义tryy[猜测]
(让[下一步(f猜测)]
(如果(足够近?猜测下一步)
下一个
(tryy next))
(tryy第一猜测)
(定点(fn[y](平均y(/x y)))
1.0))
(sqrt 10);=>3.162277660168379
对于真正的代码,您希望更改
定义
,使其行为更像R5R:允许非fn值,在
defn
defmacro
let
letfn
中可用,并验证内部定义