首页 > 解决方案 > Clojure 列表理解 - 在某些情况下只运行一次内部循环?

问题描述

有没有办法使用单个列表组合来实现以下目标,还是我需要其他方法?

(def args '([[:db/foo 1 2] [:db/bar 3 4]] {:a 1 :b 2}))


(defn myfunc []
  "Want to run inner loop only once if arg is a map."
  (for [arg args [cmd & params] arg] ; 'cmd & params' are meaningless if map.
    (if (map? arg)
      arg
      (into [cmd] params)))) 

上面的代码产生

=> [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2} {:a 1, :b 2}]

但我其实想要

=> [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2}]

显然这不是完整的功能,如果 arg 是矢量形式但希望让 map 形式直接通过(不被复制),我正在做转换。

更新:我发现列表推导源于集合构建器表示法,而“有条件地执行内部循环”实际上是一个命令式概念,因此使用单个列表推导不容易表达这一点也就不足为奇了,完全是 fp 构造。

标签: clojure

解决方案


这是您想要做的吗?: 从现有序列构建一个新序列。对于输入序列中的每个元素,有两种可能的情况:

  • 它是一个映射:在这种情况下,只需将其附加到结果序列
  • 否则它是一个序列,它的元素可能应该被转换并附加到结果序列中

如果这是您想要做的,则可以通过首先将输入序列的元素映射到子序列然后连接这些序列以形成结果序列来方便地表示此计算。

正如@cfrick 所建议的那样,mapcat将映射与连接相结合,或者通过将序列直接传递给mapcat

(mapcat (fn [x] (if (map? x) [x] x)) args)

mapcat用作换能器

(into [] (mapcat (fn [x] (if (map? x) [x] x))) args)

我认为不for适合表达这种计算。


推荐阅读