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

欢迎!请参阅关于页面,了解更多关于如何使用本站的信息。

+4
Clojure
编辑

在 Clojure/conj 谈话中,《有效程序》和《也许不是》中,Rich Hickey 指出,如果我们不知道某事,我们应该忽略它,也就是说,我们在地图中不应该有关键字-nil(spec/nilable)对。

如何处理此类情况的最佳方式是什么

(defn person
  [line]
  {:person/name   (name   line)   ;; req
   :person/height (height line)}) ;; opt, height may return nil

我想出了以下解决方案

给 'opt' 函数添加额外的arity:关键字和地图,以便它们可以条件性地关联。通过线程宏组合一切。仅适用于您“拥有”这些函数的情况。

(defn person
  [line]
  (->> {:person/name (name line)}
       (height line :person/height)))

检查调用站点并使用 some.IPAddress. 它真的很丑,我认为,想象多个可选值 - 如何避免嵌套 if-lets?"

(defn person
  [line]
  (let [ret {:person/name (name line)}]
    (if-let [height (height line)]
      (assoc ret :person/height height)
      ret)))

4 个答案

+7

选定
 
最佳答案

首先,我会问什么是 height 触发条件。根据这一点, cond-> 就进入了战场,这是我最喜欢的工具。

(defn person
  [line]
  (cond-> {:person/name (name line)}  ;; non-optional stuff goes here
    (height? line) (assoc :height (height line))
    (weight? line) (assoc :weight (weight line))
    ;; and so on for each optional thing
  ))

这是一个实例,可以在实际代码中找到(该项目中还有其他几个实例)。

另一个不错的选择是合并(merge),这是一个将多个部分(和可能为空)内容合并在一起的好工具。如果height函数返回的是nil或者是一个包含高度值的映射,那么它看起来会是这样的:

(defn person
  [line]
  (merge {:person/name (name line)}
         (height line)  ;; nil or {:height val}
         ;; and so on for each optional thing or sets of things
  ))
对于非常规律的案例,我需要在许多属性上执行相同的事情时,有时我会使用宏函数覆盖第一个示例以删除所有重复内容。这通常不太通用,所以不太会重复使用它,但如果需要,它可以使该示例变得更加简洁(就我个人而言,我并不介意重复,因为它非常清晰地表明了它在做什么)。
考虑到整个哈希表中的nil问题,在我听了Rich的演讲之后,这就促使我写了https://corfield.org/blog/2018/12/06/null-nilable-optionality/,以及seancorfield/next.jdbc如何到达保留SQL中null值在哈希表中的默认设置,同时也提供了next.jdbc.optional/as-maps作为将SQL结果集转换为省略SQL null值的哈希表的方法。

编辑
基本上,所有个人属性都连接成单行。我使用sub来取height部分,当不为str/blank?返回一些转换。cond->将执行,我只是在let中收集maybe-nil返回值。
+1

有时我会用的一种方法是简单地创建包含 nil 的映射,然后立即删除它们。

(->> {:a 10
      :b nil}
 (remove (comp nil? val))
 (into {}))
我认为(为了性能和清晰度)最好一开始就避免在集合中放入 nil 值。
当处理产生含 nil 的映射的库函数时很有用,例如 clojure.java.jdbc/query(较新的 'next-jdbc' 实现做得更好)。
0 投票

编辑了

一种解决方案是使用来自 plumatic/plumbing 库的 assoc 变体,如 assoc-when,但我想其他实用库中可能有类似函数。

https://github.com/plumatic/plumbing/blob/master/src/plumbing/core.cljx#L147

by
`assoc-when`会将`false`也抛掉,而不仅仅是`nil`。
0 投票
by

这种模式很常见,并且是各种工具库的一部分。这是一个简单、无依赖的解决方法。

(defn ?assoc
  ([m k v] (if (nil? v) m (assoc m k v)))
  ([m k v & kvs]
  (assert (even? (count kvs)))
  (let [it (.iterator ^Iterable kvs)]
    (loop [m (?assoc m k v)]
      (if (.hasNext it)
        (recur (?assoc m (.next it) (.next it)))
        m)))))
...