是否有一种优雅的方式来创建和使用具有常量和概率字段的记录。我希望能够做类似下面的事情,其中“sampler”是一个从某个分布返回样本的函数。目标是使用户对访问的文件是持续的还是概率性的是透明的。
> (defrecord Stat [val1 val2])
> (def s1 (Stat. 1 sampler))
> (:val1 s1)
> 1
> (:val2 s1)
> 4
> (:val2 s1)
> 2
答案 0 :(得分:8)
对于自定义deftypes,关键字查找的行为可能不仅仅是在地图中查找固定值,但我强烈建议不要使用它。它不仅需要做很多工作,还会违背每个人阅读代码的期望。
然而,稍微调整一下您的要求会产生一个简单的解决方案:代替(:val1 x)
,写下(x :val1)
。现在x
可以只是一个函数,它让它接收的输入决定了它的行为:
user> (defn stat [distributions]
(fn [sample]
((get distributions sample))))
#'user/stat
user> (def s1 (stat {:val1 (constantly 1)
:val2 #(rand-int 5)}))
#'user/s1
user> (s1 :val1)
1
user> (s1 :val2)
3
user> (s1 :val2)
4
答案 1 :(得分:6)
defrecord
定义的记录的查找行为无法更改。 deftype
提供更多控制,但实现所有正确的接口需要一些工作。意识到,potemkin可让您轻松定义具有自定义行为的类似地图的内容:
(use '[potemkin :only [def-map-type]])
(def-map-type Stat [val1 val2]
(get [_ k default-value]
(case k
:val1 val1
:val2 (val2)
default-value)))
(def s1 (->Stat 1 #(rand-int 10)))
(:val1 s1) ; => 1
(:val2 s1) ; => something in [0, 9]
(get s1 :val2) ; => something in [0, 9]
您还可以定义assoc
,dissoc
和keys
,但这些数据对您的数据有意义。
答案 2 :(得分:2)
我会通过使用协议来做到这一点:
(defprotocol Sample
(sample [m]))
然后通过以下方式将协议扩展到您想要采样的任何结构:
sample
的结果现在你可以做类似的事情:
(sample [#{1 2} (partial rand-int 10) {:a 1 :b #{5 6}}])
=> [2 7 {:a 1 :b 6}]
这种方法的优点:
(take 1000 (repeatedly #(sample my-schema)))
获取1000个样本。如果您想要更详细,您还可以将种子作为附加可选参数传递给sample
函数。如果您正确地执行此操作,这将实现样本的可重复性(这对于测试非常有用,并且它使(sample x seed)
作为纯函数工作)。
答案 3 :(得分:1)
基本的Clojure数据类型的基础是它们是纯粹的 - 它们是不可变的和无状态的。对于clojure Record或hash-map(除了get
之外,没有“getter”这样的东西,当在同一个map或记录实例上使用相同的键调用时,它总是会返回相同的值)。不确定的行为本质上并不纯粹。
也就是说,你可以将不纯的程序存储在一个字段中,并调用该程序来获取你的价值。
user> (defrecord Stat [val1 val2])
user.Stat
user> (def s1 (Stat. 1 #(rand-nth [0 1 1 2 2 2 3 3 3 3 4 4 4 4 4])))
#'user/s1
user> ((:val2 s1))
1
user> ((:val2 s1))
1
user> ((:val2 s1))
3
user> ((:val2 s1))
4
user> ((:val2 s1))
4
user> ((:val2 s1))
4
user> ((:val2 s1))
3
如果你需要一个真正的不确定性getter方法(如果不能指望调用者调用该字段而不是仅仅访问它),你可以改为使用gen-class
并定义一个getter方法。