有没有办法在clojure中创建一个只在函数范围内可见的命名函数?

时间:2014-04-03 21:12:38

标签: clojure scope scheme

Scheme 中,我可以这样做:

(define (adder)
  (define (one) 1)
  (define (two) 2)
  (+ (one) (two)))

在调用adder时调用3结果one会产生错误,因为one仅在adder范围内可见。

Clojure 中,如果我做类似的事情

(defn adder []
  (defn one [] 1)
  (defn two [] 2)
  (+ (one) (two)))

onetwo将污染我的命名空间,因为defn在内部使用def,这会在当前命名空间中创建绑定。

是否有一个函数/宏在本地范围内创建命名函数?

我的问题的原因是我习惯了Scheme的工作方式。以这种方式命名我的本地函数通常会使我的代码更具可读性。

3 个答案:

答案 0 :(得分:13)

尝试letfn

  

获取函数specs和body的向量,并生成一组   函数与其名称的绑定。所有名称都可用   在函数的所有定义中,以及正文。

 (defn adder []
   (letfn [(one [] 1)
           (two [] 2)]
     (+ (one) (two))))

答案 1 :(得分:9)

除了Alex的出色答案外,任何fn都可以命名。

(defn adder []
  (let [one (fn [] 1)
        two (fn [] (+ (one) (one)))]
    (+ (one) (two))))

如果您已经有let块,这将非常有用。

如果fn引用自身,则需要自己的名称

(defn silly []
  (let [constant 5
        thing (fn thong
                ([a] (+ a constant))
                ([] (inc (thong constant))))]
    (* (thing) (thing))))

fn绑定的名称不必与其自身所知的名称相同。

答案 2 :(得分:1)

如果您想要一个当前命名空间可见但其他命名空间不可见的函数 - 您可以使用defn-

  
    

<强> DEFN -
    
    用法:( defn-name&amp; decls)
    与defn相同,产生非公开def

  

来自http://clojuredocs.org/clojure_core/clojure.core/defn-

user=> (ns test)
nil

test=> (defn- foo [] "World!")
#'test/foo

test=> (defn bar [] (str "Hello " (foo)))
#'test/bar

test=> (foo)
"World!"
test=> (bar)
"Hello World!"
test=> (ns playground)
nil
playground=> (test/bar)
"Hello World!"

;; Error will be thrown
;; var: #'test/foo is not public
playground=> (test/foo)