代码之家  ›  专栏  ›  技术社区  ›  Frank C.

Clojure析构图使用:带有限定关键字的键不起作用

  •  2
  • Frank C.  · 技术社区  · 9 年前

    我在1.7.0和1.8.0中都尝试过,Clojure似乎不使用 :keys 其密钥完全合格。我不认为这与在参数的尾部有关,因为当我切换函数参数位置时,它也不起作用。

    (ns foo.sandbox)
    
    (def foo ::foo)
    (def bar ::bar)
    
    (defn normalize-vals
      [mmap & [{:keys [foo bar] :as ops}]]
      (println "normalize-vals " ops " and foo " foo " bar" bar))
    
    (normalize-vals {} {foo 1 bar 2})
    
    => normalize-vals  {:foo.sandbox/foo 1, :foo.sandbox/bar 2}  and foo  nil  bar  nil
    

    然而这起作用:

        (defn normalize-vals
          [mmap & [{a foo b bar :as ops}]]
          (println "normalize-vals " ops " and foo " a " bar" b))
    
        (normalize-vals {} {foo 1 bar 2})
    
    => normalize-vals  {:cmt.sandbox/foo 1, :cmt.sandbox/bar 2}  and foo  1  bar  2
    

    这是缺陷吗?

    2 回复  |  直到 9 年前
        1
  •  1
  •   muhuk    9 年前

    让我们将您的函数视为:

    (defn normalize-vals
      [mmap & [{:keys [foo bar] :as ops}]]
      (println "normalize-vals " ops " and foo " foo " bar" bar))
    

    请注意 foo & bar 以上是本地绑定,它们不引用函数之外的任何内容。

    …并稍微重写其他代码位:

    (def foo-const ::foo)
    (def bar-const ::bar)      
    

    不要太在意这里的命名,重点是使用不同的名称。

    (normalize-vals {} {foo 1 bar 2})
    ;; error: ...Unable to resolve symbol: foo in this context...
    
    (normalize-vals {} {foo-const 1 bar-const 2})
    ;; prints: normalize-vals  {:user/foo 1, :user/bar 2}  and foo  nil  bar nil
    

    教训应该是尽可能使用唯一的名称。


    为什么第二种变型有效?

    以破坏形式 {a foo b bar :as ops} ;

    • a & b 是新的本地绑定。如果我们有一个名为 b 这些将在该函数的范围内覆盖它们。
    • foo公司 & 酒吧 从环境中解决。如果我们不给它们加后缀 -const 如上所述,我们将得到 CompilerException 就像上面的那个。
        2
  •  0
  •   Symfrog    9 年前

    您正在使用非限定关键字进行破坏,因此不要:

    [mmap & [{:keys [foo bar] :as ops}]]

    你应该使用

    [mmap & [{:keys [::foo ::bar] :as ops}]]

    您可以使用 clojure.walk/macroexpand-all 扩展 normalize-vals :

    (clojure.walk/macroexpand-all '(defn normalize-vals
                                     [mmap & [{:keys [foo bar] :as ops}]]
                                     (println "normalize-vals " ops " and foo " foo " bar" bar)))
    
    => (def normalize-vals (fn* ([mmap & p__26720] (let* [vec__26721 p__26720 map__26722 (clojure.core/nth vec__26721 0 nil) map__26722 (if (clojure.core/seq? map__26722) (. clojure.lang.PersistentHashMap create (clojure.core/seq map__26722)) map__26722) ops map__26722 foo (clojure.core/get map__26722 :foo) bar (clojure.core/get map__26722 :bar)] (println "normalize-vals " ops " and foo " foo " bar" bar)))))
    

    需要注意的扩展的重要部分是:

    foo (clojure.core/get map__26722 :foo) 
    bar (clojure.core/get map__26722 :bar)
    

    因此,映射析构化中的键在 编译时间 命名空间中的foo和bar变量的值将不会被使用。