Statistics Clojure中的惯用模式函数
我正在学习Clojure,想听听关于惯用用法的建议。作为一个小型统计软件包的一部分,我有一个函数来计算一组数据的模式。(背景:模式是一组数据中最常见的值。计算模式的算法几乎有十几种。这里使用的是伯纳德·罗斯纳(Bernard Rosner)出版的第六版《生物统计学基础》中的一种。) 有几件事我有疑问:Statistics Clojure中的惯用模式函数,statistics,clojure,Statistics,Clojure,我正在学习Clojure,想听听关于惯用用法的建议。作为一个小型统计软件包的一部分,我有一个函数来计算一组数据的模式。(背景:模式是一组数据中最常见的值。计算模式的算法几乎有十几种。这里使用的是伯纳德·罗斯纳(Bernard Rosner)出版的第六版《生物统计学基础》中的一种。) 有几件事我有疑问: 争论。该函数接受单个序列。接受可变数量的参数(如加法函数)是否更为惯用 代码气味。看起来“let”比它应该的要复杂一点——这么多变量赋值。我是否遗漏了语言或库的任何明显(或不太明显)的用法,这些用
提前谢谢你的帮助。我觉得很好。我会换新的
f (fn [x] (not (nil? x)))
mode (filter f (map #(if (= mx (get amap %)) %) k))
与
(我不知道为什么像not nil?
这样的东西不在clojure.core
中;它是人们每天都需要的东西。)
如果存在单个唯一模式,则返回该模式。如果有多个模式,它们将作为列表返回。如果没有模式,即所有元素以相同频率出现,则返回nil。”
您可以考虑每次只返回一个seq(一个元素或空就可以了);否则,必须通过调用代码来区分情况。通过始终返回seq,您的结果将神奇地作为预期seq的其他函数的参数。以下是我的观点:
(defn mode [aseq]
(let [amap (tally-map aseq)
mx (apply max (vals amap))
modes (map key (filter #(= mx (val %)) amap))
c (count modes)]
(cond
(= c 1) (first modes)
(= c (count amap)) nil
:default modes)))
您可以使用identity函数(除非您的数据包含逻辑上为false的值),而不是定义函数f。但您甚至不需要它。我发现模式有不同的方式,这对我来说更容易理解:map amap充当一系列map条目(键值对).首先,我只过滤那些值为mx的条目。然后,我将键函数映射到这些条目上,并给出一系列键
为了检查是否有任何模式,我不再在地图上循环。相反,我只是比较模式的数量和地图条目的数量。如果它们相等,所有元素的频率都相同
以下是始终返回seq的函数:
(defn modes [aseq]
(let [amap (tally-map aseq)
mx (apply max (vals amap))
modes (map key (filter #(= mx (val %)) amap))]
(when (< (count modes) (count amap)) modes)))
(defn模式[aseq]
(让[amap(理货图aseq)
mx(应用最大值(VAL amap))
模式(映射键(过滤器#(=mx(val%))amap))]
(当(<(计数模式)(计数amap))模式时)
在我看来,将某个函数映射到一个集合,然后立即将列表压缩为一个项目是使用reduce
的标志
(defn tally-map [coll]
(reduce (fn [h n]
(assoc h n (inc (h n 0))))
{} coll))
在本例中,我将编写模式
fn,以将单个集合作为参数,就像您所做的那样。我能想到的对这样的函数使用多个参数的唯一原因是,如果您计划大量键入文字参数
因此,例如,如果这是一个交互式REPL脚本,并且您经常按字面意思键入(模式[1 2 1 2 3])
,那么您应该让函数获取多个参数,以避免您键入额外的[]
始终在函数调用中。如果您计划从文件中读取大量数字,然后采用这些数字的模式,则让函数采用一个集合参数,这样您就可以避免一直使用apply
。我猜您最常见的用例是后者。我相信apply
还增加了开销,当函数调用采用集合参数时,可以避免这种开销
我同意其他人的观点,你应该让
模式
返回一个结果列表,即使只有一个结果;这会让你的生活更轻松。当你使用时,也许可以将其重命名为模式
。这里是模式
的一个很好的简明实现:
(defn mode [data]
(first (last (sort-by second (frequencies data)))))
这充分利用了以下事实:
函数返回值->频率的映射frequencies
- 可以将映射视为键值对序列
- 如果按值对该序列进行排序(每对中的
第二个
项),则序列中的最后一项将表示模式
分区by
,以保持所有值的最大频率:
(defn modes [data]
(->> data
frequencies
(sort-by second)
(partition-by second)
last
(map first)))
“您定义的函数实际上是标识函数(因为nil在逻辑上是false)。”不,远不是。比较(map identity[true-false-nil 1])和(map#(not(nil?%1))[true-false-nil 1]的结果)。你是对的,当然,这不是同一个函数。我的意思是说,他可以在这个示例中使用标识函数。我会更正。感谢你的分析和建议。这正是我所寻找的视角的转变。我接受了你的建议,将我的第二个函数重命名为modes.:-(inc(或(hn)0))与(inc(hn0))相同:)哦,对了,我总是忘记默认值选项。谢谢。你是对的,最常见的用例是拉入成堆的数字并弹出结果。谢谢你关于改进理货图的建议。我一直在使用它,任何改进都是值得赞赏的。谢谢你的建议。返回值的方式是这样的
(defn mode [data]
(first (last (sort-by second (frequencies data)))))
(defn modes [data]
(->> data
frequencies
(sort-by second)
(partition-by second)
last
(map first)))