以下是Clojure代码:
(reduce (fn [r x] (if (nil? x) r (conj r x)))
[]
[:mouse nil :duck nil :lory nil])
在REPL中,它的求值为[:mouse :duck :lory]
。
我的问题是,代码是如何评估的?
根据我的理解,r
是[]
,x
是[:mouse nil :duck nil :lory nil]
。nil? x
为假,因此计算结果为(conj r x)
。但是x是一个向量,而不是一个元素,那么它如何将一个元素添加到conj
中的空向量r中呢?我不知道,但我的方法有些地方错了。输出是不带nil值的动物名称向量。谁能给我解释一下代码的执行。谢谢。
你的问题似乎是理解reduce
是如何工作的。我想让您参考源代码,但它只是映射到Java实现,所以我不得不伪造它。
您正在执行的reduce
类型-提供初始值-可能已编码如下:
(defn reduce [f init coll]
(loop [acc init, tail coll]
(if (seq tail)
(recur (f acc (first tail)) (rest tail))
acc)))
可以看到,它通过序列coll
工作,将函数f
应用于acc
和序列中的第一个值,以生成新的acc
。当没有更多的序列时,它返回acc
。
这如何适用于你的例子?
(fn [r x] (if (nil? x) r (conj r x)))
…忽略nil
,x
s,但conj
s在累积向量r
的末尾。您的代码或多或少相当于…
(remove nil? [:mouse nil :duck nil :lory nil])
=> (:mouse :duck :lory)
…除了你得到的是一个惰性序列而不是一个向量。
由FredOverflow的Clojure Transducers提供,您可以将减少fn
包装在这个logging
函数中,以在每个步骤中打印不同的参数(r和x):
(defn logging [f]
(fn [& args]
(prn args)
(apply f args)))
(reduce (logging (fn [r x] (if (nil? x) r (conj r x))))
[]
[:mouse nil :duck nil :lory nil])
用户=比;([]:鼠标)
([: 鼠标]nil)
([: 老鼠:鸭)
([: 鼠:鸭]nil)
([: 老鼠:鸭]:洛里)
([: 老鼠:鸭:洛里]nil)
所以只添加关键字(不是nil),从[]
开始。最后返回[:mouse :duck :lory]