按关键字的惯用clojure映射查找

按关键字的惯用clojure映射查找,clojure,Clojure,假设我有一个clojure地图,它使用关键字作为键: (def my-car {:color "candy-apple red" :horsepower 450}) 我知道我可以通过使用关键字或映射作为函数,另一个作为其参数来查找与关键字关联的值: (my-car :color) ; => "candy-apple red" (:color my-car) ; => "candy-apple red" 我意识到这两种形式在某些情况下都可以派上用场,但对于上面所示的简单用法,其中一

假设我有一个clojure地图,它使用关键字作为键:

(def my-car {:color "candy-apple red" :horsepower 450})
我知道我可以通过使用关键字或映射作为函数,另一个作为其参数来查找与关键字关联的值:

(my-car :color)
; => "candy-apple red"
(:color my-car)
; => "candy-apple red"

我意识到这两种形式在某些情况下都可以派上用场,但对于上面所示的简单用法,其中一种被认为更为惯用吗?

我认为这两种形式都是惯用的。唯一需要注意的是,第二种形式只适用于关键字。我认为,这是一个经过深思熟虑的设计选择,这将使它更有理由成为习惯用语。

(:给我的汽车上色)
是相当标准的。这其中有几个原因,我不会一一列举。但这里有一个例子

因为
:color
是一个常量,而
我的车
不是,所以hotspot可以完全内联
color.invoke(m)
的动态调度,这是
m.invoke(color)
(在一些java伪代码中)无法做到的

如果
mycar
有时恰好是一个带有
color
字段而不是普通映射的记录,那么情况就更好了:clojure编译器可以发出代码来检查“嘿,如果
mycar
是CarType的一个实例,那么只需返回
mycar.color
;否则执行所有复杂、缓慢的hashmap查找。”

来自:

  • 使用关键字优先语法访问对象的属性:

    (:property object-like-map)
    
  • 使用集合优先语法从集合中提取值(如果集合可能为nil,则使用get)


我列出了一系列支持和反对这两种形式的论点。(编辑:添加了第三个选项-
(get map:key)
,这是我新的最爱,尽管有点冗长)

(:键映射)的参数 1) 在编码标准中要求

2) 当map为nil时仍然有效

> (:a nil)
  nil
> (nil :a)
  ERROR: can't call nil
---反驳——如果key可以是nil,其他形式更好

> ({:a "b"} nil)
  nil
> (nil {:a "b"})
  ERROR: can't call nil
3) 在对象集合上执行线程和映射时效果更好

(-> my-map
  :alpha
  fn-on-alpha
  :beta
  fn-on-beta
  :gamma

> (def map-collection '({:key "values"} {:key "in"} {:key "collection"}))
> (map :key map-collection)
  ("values" "in" "collection")
---反论点---线程的代码结构不同于 通常,不同的习惯用法倾向可以应用于地图访问 必要时

4) 潜在的优化效益?(需要核实)

的参数(映射:键) 1) 键为非关键字或nil时不引发错误

> ({:a "b"} nil)
  nil
> (nil {:a "b"})
  ERROR: can't call nil
> ({"a" "b"} "a")
  "b"
> ("a" {"a" "b"})
  ERROR: string cannot be cast to IFn
2) Clojure中列表访问的一致性

> ([:a :b :c] 1)
  :b
> (1 [:a :b :c])
  ERROR: long cannot be cast to IFn
3) 与其他对象访问形式的相似性

java>         my_obj  .alpha  .beta  .gamma  .delta
clj >     ((((my-map  :alpha) :beta) :gamma) :delta)
clj > (get-in my-map [:alpha  :beta  :gamma  :delta])
cljs> (aget   js-obj  "alpha" "beta" "gamma" "delta")
4) 访问同一地图中的多个关键点时的对齐(分隔线)

---反参数---从多个映射访问同一个键时对齐更差

> (my-func
    (:key map-un)
    (:key map-deux)
    (:key map-trois)
    (:key map-quatre)
    (:key map-cinq)
> (my-func
    (map-un :key)
    (map-deux :key)
    (map-trois :key)
    (map-quatre :key)
    (map-cinq :key)
的参数(get map:key) 1) 如果arg1是map/vector/nil,arg2是key/index/nil,则不会导致错误

> (get nil :a)
  nil
> (get nil nil)
  nil
> (get {:a "b"} nil)
  nil
> (get {:a "b"} :q)
  nil
> (get [:a :b :c] nil)
  nil
> (get [:a :b :c] 5)
  nil
2) 与其他Clojure函数在形式上的一致性

> (get {:a "b"} :a)
  :b
> (contains? {:a "b"} :a)
  true
> (nth [:a :b :c] 1)
  :b
> (conj [:a :b] :c)
  [:a :b :c]
3) “地图优先”的对齐好处

> (my-func
    (get my-map :un)
    (get my-map :deux)
    (get my-map :trois)
    (get my-map :quatre)
    (get my-map :cinq))
4) Get-in可用于通过单个调用进行嵌套访问

> (get-in my-map [:alpha  :beta  :gamma  :delta])
> (aget   js-obj  "alpha" "beta" "gamma" "delta")

来源:测试

它也适用于符号,以及通过在关联数据库中查找自身来实现IFn的任何其他情况<代码>(让[k'键符号,m{k1}](km));=>1。同样地,
(mk)
表单对记录不起作用,因为它们没有实现IFn(有充分的理由,即使这很不方便)。对我来说,最令人信服的理由是:(:color nil)返回nil,而(nil:color)抛出异常。@user100464但对于另一个表单也可能有同样的理由!如果您正在动态访问密钥,
({:color“red”}nil)
返回
nil
,而
(nil{:color“red”})抛出异常。根据我的测试结果,在REPL时,
(get hash map key)
是最快的,
(hash map key)
是第二位的,
(key hash map)
是这三种形式中速度最慢的一种。结果非常一致。仅供参考,当使用关键字作为函数(即:
(:foo{:bar“baz}4))时,“如果找不到值,则可以提供默认值”也是正确的
返回
4
@ChrisOakman你说得对。提供默认值可用于所有形式的访问,因此我将其从列表中删除。你能解释“类似对象的地图”和“类似集合的地图”之间的区别吗?@Peeja我不是100%确定,但我解释了“类似对象的地图”“作为具有预定义键及其属性的映射,例如具有作为Person对象的
{:first,:last last,:age}
,同时具有作为键从数据库读取的人名的映射以及作为值的信息的映射,作为“类似集合的映射”的示例。”。还要注意,从数据库读取的名称更可能是字符串而不是关键字,因此无论如何都不能使用第一个查找表单。
> (my-func
    (get my-map :un)
    (get my-map :deux)
    (get my-map :trois)
    (get my-map :quatre)
    (get my-map :cinq))
> (get-in my-map [:alpha  :beta  :gamma  :delta])
> (aget   js-obj  "alpha" "beta" "gamma" "delta")