我正在学习 Clojure,想要一些关于惯用用法的建议。作为小型统计包的一部分,我有一个计算一组数据的众数的函数。 (背景:众数是一组数据中最常见的值。几乎有十几种已发布的算法来计算它。这里使用的算法来自 Bernard Rosner 的《生物统计学基础》第 6 版。)
(defn tally-map
" Create a map where the keys are all of the unique elements in the input
sequence and the values represent the number of times those elements
occur. Note that the keys may not be formatted as conventional Clojure
keys, i.e. a colon preceding a symbol."
[aseq]
(apply merge-with + (map (fn [x] {x 1}) aseq)))
(defn mode
" Calculate the mode. Rosner p. 13. The mode is problematic in that it may
not be unique and may not exist at all for a particular group of data.
If there is a single unique mode, it is returned. If there are multiple
modes, they are returned as a list. If there is no mode, that is all
elements are present in equal frequency, nil is returned."
[aseq]
(let [amap (tally-map aseq)
mx (apply max (vals amap))
k (keys amap)
f (fn [x] (not (nil? x)))
modes (filter f (map #(if (= mx (get amap %)) %) k))
]
(cond (= 1 (count modes)) (first modes)
(every? #(= mx %) (vals amap)) nil
:else modes)
)
)
我有几个问题:
- 论点。该函数接受单个序列。像加法函数这样接受可变数量的参数是否更惯用?
- 代码气味。看起来“let”比它应该的要复杂一些——这么多的变量赋值。我是否错过了语言或库的任何明显(或不那么明显)的用法,这些用法将使该方法更加简洁?
先谢谢您的帮助。
在我看来,将某些函数映射到集合上,然后立即将列表压缩为一项是一个使用的标志reduce
.
(defn tally-map [coll]
(reduce (fn [h n]
(assoc h n (inc (h n 0))))
{} coll))
在这种情况下我会写mode
fn 将单个集合作为参数,就像您所做的那样。我能想到对这样的函数使用多个参数的唯一原因是,如果您计划必须大量键入文字参数。
所以如果例如这是一个交互式 REPL 脚本,您经常会输入(mode [1 2 1 2 3])
从字面上看,那么您应该让该函数接受多个参数,以免您输入额外的内容[]
一直在函数调用中。如果您计划从文件中读取大量数字,然后采用这些数字的众数,则让该函数采用一个作为集合的参数,这样您就可以避免使用apply
每时每刻。我猜你最常见的用例是后者。我相信apply
还增加了当您有一个采用集合参数的函数调用时可以避免的开销。
我同意其他人的观点,你应该有mode
即使只有一个结果,也返回结果列表;它会让你的生活更轻松。也许重命名它modes
当你在做的时候。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)