Clojure destructure map使用:具有限定关键字的键不起作用

Clojure destructure map使用:具有限定关键字的键不起作用,clojure,Clojure,我在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 " f

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

这是一个缺陷吗?

您正在使用不合格的关键字来分解结构,因此:

[mmap&[{:键[foo-bar]:as-ops}]

你应该使用

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

您可以使用
clojure.walk/macroexpand all
展开
标准化VAL

(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)

因此,map destructuring中的键将在编译时转换为关键字,而命名空间中的foo和bar变量中的值将不被使用

让我们将您的函数视为:

(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
    是新的本地绑定。如果我们有一个名为
    a
    b
    的变量,这些变量将在该函数的范围内覆盖它们
  • foo
    bar
    从环境中解析。如果我们不像上面那样用
    -const
    作为后缀,我们将得到一个
    编译器异常
    ,就像上面的一样

foo和bar的定义(
def
)?@FrankC。解构将创建新的本地绑定来隐藏foo和bar限定的关键字变量。第二个条件有效(即不带“:keys”),因此我将其解释为:keys执行覆盖定义的本地绑定,是吗?@FrankC。是的,keys destructuring被扩展为一种let形式,它将密钥转换为关键字,并在编译时在映射中查找它们,并且不使用密钥的运行时值在映射中查找值,我将用一些详细信息更新我的答案。谢谢,按回答检查!FWIW,Clojure 1.9 beta版对带有合格密钥的地图提供了额外的支持: