【问题标题】:Clojure destructure map using :keys with qualified keywords not workingClojure 解构映射使用 :keys 和合格的关键字不起作用
【发布时间】:2016-02-27 11:32:00
【问题描述】:

我在 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

这是缺陷吗?

【问题讨论】:

标签: clojure


【解决方案1】:

让我们按原样处理你的函数:

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

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

...并稍微改写其他代码:

(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 是新的本地绑定。如果我们有一个名为 ab 的 var,它们将在此函数的范围内覆盖它们。
  • foo & bar 从环境中解析。如果我们不像上面那样在它们后面加上-const,我们会得到一个CompilerException,就像上面那样。

【讨论】:

    【解决方案2】:

    您正在使用不合格的关键字进行解构,所以不要:

    [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 变量的值将不会被使用。

    【讨论】:

    • foo 和 bar 在其定义中是合格的 (def)?
    • @FrankC。解构将创建新的本地绑定,隐藏 foo 和 bar 限定的关键字变量。
    • 第二个条件有效(即没有 ':keys`)所以我将其解释为 :keys 是否覆盖定义的本地绑定,是吗?
    • @FrankC。是的,键解构扩展为 let 形式,将键转换为关键字并在 编译时在映射中查找它们,并且不使用键的运行时值在映射中查找值,我会用一些细节更新我的答案。
    • 谢谢,已回答!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-25
    • 1970-01-01
    • 1970-01-01
    • 2011-12-13
    • 1970-01-01
    • 1970-01-01
    • 2017-09-13
    相关资源
    最近更新 更多