7

为了更好地理解mapcat,我举了一个例子:

user>  (mapcat #(list % %) [1 2 3])
(1 1 2 2 3 3)

并试图重现文档描述的内容,故意使用mapconcat

user> (doc mapcat)
clojure.core/mapcat
([f & colls])
  Returns the result of applying concat to the result of applying map
  to f and colls.  Thus function f should return a collection.

通过做这个:

user>  (concat (map #(list % %) [1 2 3]))
((1 1) (2 2) (3 3))

但是,如您所见,它不起作用。但是,我可以像这样使用 reduce,但不知道它是否正确:

user>  (reduce #(concat %1 %2) (map #(vec (list % %)) [1 2 3]))
(1 1 2 2 3 3)

以上方法有效,但我不知道这是否是使用mapconcat重新创建 mapcat的正确方法。

基本上我想了解mapcat的工作原理。

发生了什么,如何访问mapcat的源代码?(我正在使用 Emacs + nrepl)

4

2 回答 2

6
user=> (source mapcat)
(defn mapcat
  "Returns the result of applying concat to the result of applying map
  to f and colls.  Thus function f should return a collection."
  {:added "1.0"}
  [f & colls]
    (apply concat (apply map f colls)))
nil
user=> 

原因reduce也有效,因为它有效:

(concat (concat '(1 1) '(2 2)) '(3 3))

apply,如源代码中所用,扩展为:

(concat '(1 1) '(2 2) '(3 3))

在您最初的尝试中concat

  user=> (map #(list % %) [1 2 3])
  ((1 1) (2 2) (3 3))
  user=> (concat (list '(1 1) '(2 2) '(3 3)))
  ((1 1) (2 2) (3 3))
  user=> (concat [1])
  (1)

您可以看到,如果您concat使用单个参数调用,它会返回该参数。

于 2012-11-05T01:22:57.377 回答
2

concat是一个可变参数函数,即它可以采用 n 个参数,其中每个参数是一个值序列,即签名变为(defn concat [& lst]]). 而在您的示例中,您使用单个参数调用 concat ,假设 concat 需要连接一个 seq 的值,这就是您得到结果的原因,即返回相同的列表列表。

(apply concat(apply map #(list % %) [1 2]))不会工作。

(apply map #(list % %) [[1 2]])(apply map #(list % %) [1 2] [])将工作。

这是因为 apply 期望最后一个参数是一个值序列,并且该值序列中的每个项目都将作为参数传递给应用函数。在您应用失败的情况下,调用将扩展(map #(list % %) 1 2)为错误的,并且错误消息还显示它无法将 long 转换为序列,因为映射需要序列作为参数。

于 2012-11-05T04:21:20.383 回答