在Clojure中,有没有一种方式可以创建一个仅在函数作用域内可见的命名函数?

4
在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个回答

13

尝试使用letfn

接受一个函数规范向量和一个主体,并生成一组将函数绑定到其名称的绑定。 所有名称都在所有函数的定义以及主体中可用。

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

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))))
< p > fn 绑定的名称不一定与它自己知道的名称相同。


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

defn-

用法: (defn- 名称 & 声明)
与defn相同,产生非公共定义

来自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)

我只是在谈论函数作用域。虽然我感谢你指出了这个选项,但defn-在这里不适用。 - Adam Arold
没问题。像其他人所说的那样,letletfn肯定是你要找的。 - Scott

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接