【问题标题】:var defined inside a function is still accessible outside of the function in clojure, why?在函数内部定义的 var 仍然可以在 clojure 中的函数外部访问,为什么?
【发布时间】:2017-11-01 00:31:04
【问题描述】:

在 Clojure REPL 中:

user> (defn add [x y] (def foo :defined-inside) (+ x y))
#'user/add
user> foo
#object[clojure.lang.Var$Unbound 0x63a0ad69 "Unbound: #'user/foo"]
user> (add 1 1)
2
user> foo
:defined-inside

显然,我认为 foo 不应该在 add 之外访问,为什么 Clojure 允许这样做,这与其他 lisp(例如方案)不同?

【问题讨论】:

  • 其他 lisp... CL defparameter, defun, defvar 创建顶级绑定。 Scheme 是唯一具有这种奇怪功能的方案,即 lambda 内的 definelambda 宏处理(并重写为 letrec 最终变成 lambda 形式),而顶级 define 是由自己的宏处理。
  • @Sylwester 对我来说,我认为这是一个坏主意,它会污染全球环境,而 Scheme 是正确的选择。
  • 如果你想要一个局部变量或函数,你永远不会有礼貌,因为你不使用def,与 Common Lisp 相同。这与在具有 较少混淆 的 Scheme 中相同(因为您可能有这样的印象,definelet 中的顶层相同,但它是由两个完全不同的部分完成的除了名称冲突之外彼此无关的实现)

标签: clojure scheme


【解决方案1】:

def 在 Clojure 中始终是全局的,这与其他一些 Lisps 不同。

要在函数中创建本地符号绑定,请使用let

【讨论】:

  • 为什么 Clojure 是这样设计的,有什么原因吗?对我来说,我认为这是一个坏主意,它会污染全球环境。
  • 因为 Clojure 喜欢给你足够的绳子让你上吊。有时使用各种奇怪的副作用要容易得多;你必须清楚地表达它,但是如果你愿意的话,Rich Hickey 会让你这样做。
【解决方案2】:

在 Clojure 中,您可以使用 let 定义本地变量:

(defn add [x y] 
  (let [foo :defined-inside] 
    (+ x y)))

它甚至可以用于本地函数:

(defn add [x y]
  (let [foo (fn [x y] (+ x y))]
    (foo x y)))

但是对于相互递归的函数你需要使用letfn:

(defn is-odd? [n]
  (letfn [(is-even? [n]
            (if (zero? n)
                true
                (is-odd? (dec n))))
          (is-odd? [n]
            (if (zero? n)
                false
                (is-even? (dec n))))]
    (is-odd? n)))

【讨论】:

    【解决方案3】:

    函数内部的Def很有用,当我想通过调用函数lazily在全局变量中创建绑定时。

    (defn initialize-browser []
        (def browser (run-browser)))
    

    例如,我使用这种模式来运行带有 selenium 的浏览器。 我可以轻松控制浏览器的状态。 如果我不想运行浏览器,我不会调用该函数。当我想运行和处理一个浏览器时,我只调用这个函数一次,并由其他函数处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-23
      • 1970-01-01
      • 1970-01-01
      • 2014-03-11
      • 2017-01-04
      相关资源
      最近更新 更多