将地图列表简化为Clojure中的列表

时间:2019-02-24 03:30:18

标签: clojure

几周前我已经开始进行一些函数式编程,并且我正在尝试从一个映射列表到一个考虑clojure中特定键的列表执行映射。

我的地图列表如下:'({:a "a1" :b "b1" :c "c1"} {:a "a2" :b "b2" :c "c2"} {:a "a3" :b "b3" :c "c3"})

我想要得到的输出是:'("b1" "b2" "b3")

我尝试了以下操作:

(doseq [m maps]
  (println (list (get m :b))))

我的输出是一个列表列表(在为每次迭代创建一个列表时,这是预期的)。所以我的问题是,如何将其简化为一个列表?

更新

只需尝试以下操作:

(let [x '()]
  (doseq [m map]
    (conj x (get m :b))))

但是,它仍然无法正常工作。我不明白这一点,因为我期望将元素添加到一个空列表中

3 个答案:

答案 0 :(得分:7)

这是生产Clojure代码中非常常见的模式,因此是学习的好地方。通常,请查看https://clojure.org/reference/sequences上有关序列的文档,当遇到类似任务时,请查看哪种模式最适合并探索该组中的功能。在这种情况下,它是“处理序列的每个项目以创建新的序列”,并且列出的第一项是map

您的示例可能像

 (map :b my-data)

答案 1 :(得分:0)

您有正确的想法,但使用的是错误的功能。 doseq仅用于副作用,并且始终返回nil。您要查找的函数是for,它将一个序列作为输入并返回另一个序列作为输出。我通常更喜欢for而不是类似的map,因为for可以命名循环变量:

(def data-list
  [{:a "a1" :b "b1" :c "c1"}
   {:a "a2" :b "b2" :c "c2"}
   {:a "a3" :b "b3" :c "c3"}])

(let [result (vec (for [item data-list]
                     (:b item)))]
  (println result)   ; print result
  result)            ; return result from `let` expression

result => ["b1" "b2" "b3"]

相反,您可以这样做:

(println
  (doseq [item data-list]
    (println (:b item))))

您可以看到doseqfor的区别:

b1    ; loop item #1
b2    ; loop item #2
b3    ; loop item #3
nil   ; return value of doseq

有关在线详细信息,请参见https://www.braveclojure.com/,并购买《 Getting Clojure》等一本好书(或5本)。

答案 2 :(得分:0)

(doseq [m maps]
  (println (list (get m :b))))

在短短的两行中,您打破了函数式编程的一些一般规则:

  • 将数据作为参数传递给函数,而不作为对全局变量的引用 变量。
  • 不打印计算结果。返回它们的值 功能。
  • 避免使用诸如doseq之类具有副作用的机制。

尽管如此,您离解决方案也不远。 doseq本质上是for的一个版本,会丢弃其结果。如果将doseq替换为for,并且摆脱printlnlist,我们得到

=> (for [m maps] (get m :b))
("b1" "b2" "b3")

但是Arthur Ulfeldt's simple use of map更好。