Clojure destructure map使用:具有限定关键字的键不起作用
我在1.7.0和1.8.0中都尝试过这一点,Clojure似乎不会使用键完全限定的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
: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
,就像上面的一样编译器异常
def
)?@FrankC。解构将创建新的本地绑定来隐藏foo和bar限定的关键字变量。第二个条件有效(即不带“:keys”),因此我将其解释为:keys执行覆盖定义的本地绑定,是吗?@FrankC。是的,keys destructuring被扩展为一种let形式,它将密钥转换为关键字,并在编译时在映射中查找它们,并且不使用密钥的运行时值在映射中查找值,我将用一些详细信息更新我的答案。谢谢,按回答检查!FWIW,Clojure 1.9 beta版对带有合格密钥的地图提供了额外的支持: