在Clojure中重载关键字

6

我有这样一张地图:

(def a {:a 1, :b 2})

我希望能够重载地图,使一些关键字执行函数,以便实现以下目标:

(c: a)

能够执行函数,这是可能的吗?

更新:

我意识到我可以做类似于以下的事情:

(def a {:a (fn[] 1) :b (fn[] 2) :c (fn[] x)})

:and:

((c: a))

但是我必须将我现有的所有地图条目都转换为函数。

我希望每次都重新评估函数。例如,当我执行以下操作时:

(def ab{:a 1         :b 2         :c ( #(.nextInt (java.util.Random.) 1000))}) 

(str (:c ab) " " (:c ab) " " (:c ab))

I get:

61 61 61

更新 2

我考虑了所给答案并意识到他是正确的,我应该只使用不可变的结构。最终的解决方案是创建一个“enrich”函数来按需创建动态属性。

不再使用三个不同的数字。

 (def a {:a 1, :b 2})

我希望能够重载地图,使得一些关键词能够执行函数,以此来实现:

(str (:c (enrich ab)) " " (:c (enrich ab)) " " (:c (enrich ab)))

每次运行都会产生不同的数字,如下所示:
58 639 710
3个回答

4

我相信,如果你把数据结构定义为记录而不是常规映射,就可以覆盖关联查找的行为。

你基本上需要覆盖clojure.lang.ILookup:详见这个问题以获取更多细节。

这里有一个快速示例:

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

我确实尝试过,但它不允许我将项目关联到其中。虽然我可能无法使用您的答案,但我还是选择了它,因为我对您真正尝试做到这一点印象深刻 :) - yazzapps.com

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"

2

使用同一张地图有时传递不可变值,有时传递不纯函数,我认为这与不可变映射应该使用的精神相悖。因此,我建议使用引用类型,它指向仅包含数据值的不可变映射。然后,当这些数据值需要改变时,将引用类型指向反映任何更改的新不可变映射。


当访问该字段时,我希望函数被评估,因为返回的值是计算出来的并且可能会改变。我知道这不是不可变的,就像我的简单示例一样,但我只是展示了一个简单的情况。 - yazzapps.com
1
哦,好的,那么您想要评估的函数不是纯函数。 - dbyrne
是的,抱歉,在我的原始问题中我没有提到那个。 - yazzapps.com

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接