【问题标题】:Clojure list comprehension - run inner loop only once in certain circumstance?Clojure 列表理解 - 在某些情况下只运行一次内部循环?
【发布时间】:2020-08-11 12:06:39
【问题描述】:

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

(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 形式直接通过(不被复制),我正在做转换。

更新:我发现list comprehension 的根源在于set builder notation,而“有条件地执行内部循环”实际上是一个命令式概念,所以这并不奇怪。用单个列表理解来表达并不容易,这完全是一个 fp 构造。

【问题讨论】:

    标签: clojure


    【解决方案1】:

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

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

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

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

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

    或使用mapcat 作为传感器

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

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

    【讨论】:

    • (mapcat (fn [x] (if (map? x) [x] x))) 因为传感器似乎做同样的事情(至少对于给定的测试用例)
    • @cfrick 我完全忘记了mapcat。这是一个很大的改进,事情变得更加简洁!
    【解决方案2】:

    在我提出解决方案之前,我对您提供的函数myfunc有几点意见:

    • 文档字符串放错了位置。我知道这很奇怪,但是文档字符串必须在函数的参数之前,所以在 [] 之前,
    • 您对for 指令的使用不是您所期望的。您应该查看the documentation here 中的示例。主要是for 指令很适合从列表中构造笛卡尔积。

    我希望我正确理解了您想要实现的目标。在我的提议中,我通过loop 指令使用递归来构建一个包含所有参数的向量:

    (defn myfunc "Want to run inner loop only once if arg is a map."
      [& args]
      (loop [arg-to-parse args
             res          []]
        (if (empty? arg-to-parse)
          res
          (let [arg              (first arg-to-parse)
                new-arg-to-parse (rest  arg-to-parse)]
            (if (map? arg)
              (recur new-arg-to-parse (conj res arg))
              (recur new-arg-to-parse (into res arg))
              )))))
    
    (myfunc [[:db/foo 1 2] [:db/bar 3 4]] {:a 1 :b 2})
    ;; => [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2}]
    (myfunc {:a 1 :b 2} [[:db/foo 1 2] [:db/bar 3 4]])
    ;; => [{:a 1, :b 2} [:db/foo 1 2] [:db/bar 3 4]]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多