2024 Clojure状态调查!中分享你的想法。

欢迎!请查看关于页面,以了解更多关于该功能的信息。

+5
集合

这是一个关于3个不同的Clojure方面的交集的问题

  1. 集合函数将nil视为空集合。如filtermap甚至assoc等函数都乐于接受nil作为集合。
  2. if及其相关宏(如whenandor等)将nil视为非真值。
  3. empty?将其输入强制转换为seq,这使得不必要的内存分配变得多余。使用seq而不是(not (empty? xs))来检查非空是常见的惯例。这通常是一个混淆的来源,因为(not (empty? xs))的意图似乎比seq更清晰。

话虽如此,我认为在应用中明确地将每个空集合表示为nil可能是有用的,因为这样

(let [xs (get-non-empty-coll-or-nil)]
  (if xs
    (do-stuff-with xs)
    (do-nothing)))

...比这样更高效、更清晰

(let [xs (get-possibly-empty-coll)]
  (if (seq xs)
    (do-stuff-with xs)
    (do-nothing)))

这种方法的两个缺点是

  1. 必须使用(fnil conj [])(fnil conj #{})而不是conj来确保集合是向量或集合,因为将元素添加到nil将创建一个列表,而我个人几乎从未使用过列表。
  2. 必须对所有传入集合的边界进行not-empty检查。

你有什么看法?

3个答案

+3

在我看来,你过分思考了这个问题。我倾向于用简洁、易读的方式编写函数,如果它们在某些情况下返回nil或空序列,那就随它去吧。除非我知道这个函数会在特定环境中使用,而这种环境确实需要它返回nil或空序列,否则我不会不遗余力地使其成为其中之一。这取决于我最简单、最明确的实现自然产生的结果。

从性能的角度来看,情况类似。这是一个微观优化,正如你提到的,为了强制实施这一点,你可能会污染代码的可读性和简洁性,例如在处理conj或其他情况时。这也似乎很难一直维持这种模式,因为它只取决于良好的意图。因此,我认为你在这里也过分思考了。同样,除非我在绝对的性能关键用例中,并且已经用尽了所有其他的方法,而且我的分析表明,去掉条件检查中的seq能节省可观的的时间,我才会考虑这样做,而且也只会针对那些在分析中显示有热点的函数。

+1

我不认为如果了解(pervasive, doc'ed)惯例使用它,那么(seq xs)就不那么清晰。我认为fnil难以心理解析,not-empty也相当笨拙。当然,我发现用seq比它们更好。

实际上,我认为一个更有影响的选择是避免在值为nil的映射中放置属性。

...