请分享您的想法,参加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' 函数添加额外的参数:关键字和映射,这样它们可以条件性地关联。通过线程宏组合所有内容。只有在你“拥有”这些函数时才适用。

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

检查调用站点并在some上关联。在我来看,这真的很丑陋,想象一下多个可选项 - 如何避免多层if-let?

(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或包含"height"键的映射,那么它看起来像:

(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
  ))
by
对于我需要在许多属性中进行相同操作的情况,我有时会使用宏将第一个示例宏化,以消除所有重复。这通常不够通用,以至于我会重复使用它,但如果需要,它可以减少该示例的重复性(就我个人而言,重复性不影响我,因为它非常清楚地表明了它在做什么)。
by
考虑到这个问题,即哈希表中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值的哈希表的方法。
by
基本上,所有的人员属性都连接成单行。我使用subs获取高度部分,当not str/blank?返回某些转换。cond->将执行,我只会在let中聚集可能是nil的返回值。
+1 投票

我有时候还会使用一种方法,就是直接创建带有 nil 的地图,然后立即将其移除。

(->> {:a 10
      :b nil}
 (remove (comp nil? val))
 (into {}))
我认为这最好(既有助于性能,也有利于清晰度),一开始就不将 nil 值放入集合中。
当处理产生带有 nil 的 map 的库函数时非常有用,例如 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)))))
...