我有这样的地图:
(def a {:a 1, :b 2})
:我希望重载地图,以便某些关键字执行以下功能:
(c: a)
可以执行一个功能。这可能吗?
我意识到我可以做类似的事情:
(def a {:a (fn[] 1) :b (fn[] 2) :c (fn[] x)})
:和
((c: a))
:但是我必须将我拥有的每个现有地图条目转换为函数。
我希望每次都能重新评估该功能。例如,当我这样做时:
(def ab{:a 1 :b 2 :c ( #(.nextInt (java.util.Random.) 1000))})
(str (:c ab) " " (:c ab) " " (:c ab))
我明白了:
61 61 61
而不是三个不同的数字
我想到了我给出的答案并意识到他是对的,我应该只使用不可变结构。我想出的最终解决方案是拥有一个“丰富”功能,可以根据需要创建动态属性。
(def a {:a 1, :b 2})
:我希望重载地图,以便某些关键字执行以下功能:
(str (:c (enrich ab)) " " (:c (enrich ab)) " " (:c (enrich ab)))
每次都会产生不同的数字:
58 639 710
答案 0 :(得分:4)
如果您将数据结构设为记录而非常规地图,我相信可以覆盖关联查找的行为。
您基本上需要覆盖clojure.lang.ILookup:有关详细信息,请参阅this question
这是一个简单的例子:
(deftype TestLookup []
clojure.lang.ILookup
(valAt [this k not-found]
(str "Generated value for key - " k))
(valAt [this k]
(.valAt this k nil)))
(def lookupable-object (TestLookup.))
(:somekey lookupable-object)
=> "Generated value for key - :somekey"
答案 1 :(得分:2)
使用相同的映射有时会传回不可变的值,有时会传回不纯函数,这似乎违背了应该使用不可变映射的精神。而不是这样做,我建议使用一个引用类型,它指向一个只有数据值的不可变映射。然后,当其中一个数据值需要不同时,将引用类型指向反映任何更改的新不可变映射。
答案 2 :(得分:2)
Clojure更喜欢纯数据结构,而不是结合数据和行为的对象。您可以通过函数访问数据结构来获得所需的行为:
(def base-foo {:a 1, :b 2})
(defn foo [key]
(if (= :c key)
(rand-int 100)
(get base-foo key)))
(str (foo :c) " " (foo :c) " " (foo :c))
;;=> "66 52 25"