gpt4 book ai didi

线程环境中的 Clojure 全局变量行为

转载 作者:行者123 更新时间:2023-12-04 14:47:45 25 4
gpt4 key购买 nike

鉴于这可以按我的预期工作:

(do
(println (resolve 'a)) ; nil
(def a "a")
(println (resolve 'a))) ; #'user/a

我想了解为什么这不会:
(future
(println (resolve 'b)) ; #'user/b (shouldn't it be still undefined at this point?)
(def b "b")
(println (resolve 'b))) ; #'user/b

我也想知道这是否是一个合适的解决方案(不完全解决同样的问题,但在我的上下文中做同样的工作):
(def c (atom nil))
(future
(println @c) ; nil
(reset! c "c")
(println @c)) ; c

最佳答案

这种行为是由 def 的方式引起的。表格被编译。

请注意,使用 def不在顶层的表单(或者可能在顶层 let 内——关于这个案例的更多评论见下文)无论如何都被视为一种风格问题。另一方面,使用 Atom 的代码片段很好——如果它可以满足您的需求,就没有理由不使用它。

转到def故事:

  • 汇编def形式:

    当一个 def遇到 form 时,编译器会在当前命名空间中创建一个适当名称的 Var。 (通过使用命名空间限定符号作为def 的名称参数来尝试def 当前命名空间之外的Var 会导致异常)。 Var 起初是未绑定(bind)的,直到 def实际执行;对于顶级 def , 马上就可以了,但是对于 def隐藏在函数体内(或在 let 表单内——见下文),这将是函数被调用的时候:
    ;;; in the user namespace:

    (defn foo []
    (def bar "asdf")
    :done)
    ; => #'user/foo

    bar
    ; => #<Unbound Unbound: #'user/bar>

    ;;; let's change the namespace and call foo:

    (ns some.ns)
    (user/foo)
    ; => :done

    bar
    ; exception, the bar Var was created in the user namespace!

    user/bar
    ; => "asdf"
    ; the Var's namespace is fixed at compile time
  • 第一个例子——do形式:

    顶级do s 被视为其内容被拼接到代码流中 do 的位置。发生。所以如果你输入 (do (println ...) (def ...) (println ...))在 REPL 中,这相当于输入第一个 println表达式,然后是 def ,然后是第二个 println表达式(除了 REPL 只产生一个新的提示)。
  • 第二个例子——future :
    (future ...)扩展到接近 (future-call (fn [] ...)) .如果 ...包括 def形式,它将以我们上面看到的方式编译。当匿名函数在自己的线程上执行时,Var 将被创建,因此 resolve将能够找到它。
  • 作为旁注,让我们看一下类似的片段及其输出:
    (let []
    (println (resolve 'c))
    (def c "c")
    (println (resolve 'c)))
    ; #'user/c
    ; #'user/c
    ; => nil

    原因和以前一样,加了 let首先编译,然后作为一个整体执行。这是使用顶级 let 时应牢记的一点。内部有定义的表单——只要没有副作用代码与定义混合,通常是可以的;否则必须格外小心。
  • 关于线程环境中的 Clojure 全局变量行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12059936/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com