可以更好地避免 nil 连接吗?

Can avoiding nil conjoins be done better?

我有一个令牌扫描器,它只是 returns nil 我不感兴趣的字符。而不是 conj nils 到我的令牌向量,然后再将它们全部剥离出来,我只想不添加它们。

我正在使用

;; dont conjoin if value false
(defn condj [v val]
  (cond-> v, val (conj val)))

做到这一点。有没有具体的算子或者更简洁的实现?

这里我认为过于简化的做法是最干净的(也稍微简洁一些):

(defn condj [v val]
  (if val (conj v val) v))

我发现这更容易快速理解。唯一的缺点是 v 是重复的,因为它没有被线程化,但是对于这样一个简单的函数来说,这并不是一个很大的损失。

我喜欢 cond-> 版本并且经常使用它来避免在 if 版本中重复。不过,不要忘记明确说明 false 值。我还喜欢使用 Plumatic Sc​​hema 明确表示进入和离开函数的数据形状:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [schema.core :as s]))

(s/defn condj :- [s/Any]
  "Conjoin an item onto a vector of results if the item is not nil."
  [accum :- [s/Any]
   item :- s/Any]
  (cond-> accum
    (not (nil? item)) (conj item)))

(dotest
  (let [result (-> []
                 (condj :a)
                 (condj :2)
                 (condj false)
                 (condj "four")
                 (condj nil)
                 (condj "last"))]
    ; nil is filtered but false is retained
    (is= result [:a :2 false "four" "last"])))

您可能还对使用 my favorite library 的另一个版本感兴趣:

(s/defn condj :- [s/Any]
  "Conjoin an item onto a vector of results if the item is not nil."
  [accum :- [s/Any]
   item :- s/Any]
  (cond-it-> accum
    (not-nil? item) (append it item)))

对于更复杂的表单,使用占位符符号 it 可以明确显示值的线程位置。我也喜欢便利函数 not-nil?append,因为它们也使代码的意图更清晰。

我相信您可以为此使用 transducers。它们被解释 here。我们的归约函数是 conj 并且我们构建了一个变换器 (remove nil?) 将此函数转换为忽略 nil:

的函数
(def condj ((remove nil?) conj))

注意removefilter相反。我们还可以使用 (filter some?) 实现 condjsome? 是一个对除 nil:

以外的任何值都成立的函数
(def condj ((filter some?) conj))

似乎有效:

user=> (condj [3 4 5] 9)
[3 4 5 9]
user=> (condj [3 4 5] nil)
[3 4 5]
user=> (condj [3 4 5] false)
[3 4 5 false]

考虑使用 into 而不是 conj:

(into [1 2 3] nil) ;=> [1 2 3]

(into [1 2 3] [4]) ;=> [1 2 3 4]

注意:缺点是您必须 return 产生一个序列,这会增加一些开销。但是,如果您忘记执行此操作,则会出现错误,当您想要附加多个项目时,可以轻松扩展逻辑,代码易于理解,并且不需要创建任何自定义函数。