请在2024年Clojure状态调查中分享您的想法!

欢迎!请查看关于页面以获取更多关于此工作方式的信息。

+5
集合

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

  1. 集合函数将nil视为空集合。像filtermap甚至assoc这样的函数愉快地接受nil作为coll。
  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或空seq,那就这样吧。除非我知道该函数将用于特定的上下文,而这确实需要它是nil或空seq,否则我不会费心去使之成为其中之一。它会是简单明了的最自然实现。

性能方面也是类似的。这是一个微观优化,正如你提到的,要强制执行它,可能会有必要牺牲代码的可读性和简洁性,例如,现在你可能需要处理 conj 或其他情况。它似乎也很难保持这种模式的一致性,因为它只依赖于良好的意图。因此,我认为你在这里也过度思考了。同样,除非我在一个绝对的性能关键情况下,并且我已经尝试了所有其他方法,我的分析表明删除条件中的 seq 用于空检查可以节省相当多的时间,否则我不会为此费心,并且仅针对分析了显示其热点的函数。

+1

我不同意 (seq xs) 的可读性更差,如果你知道(普遍的、文档化的)使用它的习语。我认为 fnil 很难以心理分析,而且 not-empty 也相当繁琐。当然,我发现使用 seq 比那些更好。

我实际上认为更重要的选择是在 map 中避免放置值为 nil 的属性。

...