【问题标题】:Clojure map-longestClojure map-最长的
【发布时间】:2010-12-16 12:58:54
【问题描述】:

我正在尝试编写一个名为 map-longest 的 Clojure 实用程序函数(感谢替代名称建议)。此函数将具有以下“签名”:

(map-longest fun missing-value-seq c1 & colls)

并且行为类似于map,除了它将继续处理提供的集合直到longest用完。对于比最长的集合短的集合,当它用完值时,它将从missing-values-seq 中获取它们。它应该是惰性的,但显然不能与无限集合一起使用。

使用示例:

(print (apply str
  (map-longest #(str %1 \space %2 \space %3 \newline) (repeatedly "--")
    ["a1" "a2" "a3"] ["b1" "b2"] ["c1" "c2" "c3" "c4"])))

应该产生以下输出:

a1 b1 c1
a2 b2 c2
a3 -- c3
-- -- c4

但我可能打错电话了。

我该如何实现呢? clojure.core 或 clojure-contrib 库是否已经有类似的东西?作为missing-value-seq 的替代方案,是否最好传入第二个函数来生成缺失值(例如:在我的示例中为#(identity "--"))?

用例:我正在编写一个小型文本蜘蛛纸牌播放器,作为学习 Clojure/函数式编程的练习。我需要能够展示游戏画面(纯粹主义者的画面:-))。

【问题讨论】:

    标签: clojure map


    【解决方案1】:

    这里有一个解决方案:

    (defn map-longest
      ([fn missing-value-fn c1]
        (map fn c1))
      ([fn missing-value-fn c1 & colls]
        (lazy-seq
          (when (not-every? empty? (conj colls c1))
            (let [firsts (map first (conj colls c1))]
              (cons
                (apply fn (map #(if (nil? %) (missing-value-fn) %) firsts))
                (apply map-longest
                  (conj (map rest colls) (rest c1) missing-value-fn fn))))))))
    

    测试:

    user=> (print (apply str 
             (map-longest #(str %1 \space %2 \space %3 \newline) #(identity "--") 
               ["a1" "a2" "a3"] ["b1" "b2"] ["c1" "c2" "c3" "c4"])))
    a1 b1 c1
    a2 b2 c2
    a3 -- c3
    -- -- c4
    nil
    

    请注意,我采用的是missing-value-fn 方法,而不是missing-value-seq 方法。

    更新

    更新了代码以处理 ffriend 在 cmets 中提到的情况。

    测试:

    user=> (print (apply str
              (map-longest #(str %1 \space %2 \space %3 \newline) #(identity "--")
                ["a1" "a2" nil] ["b1" "b2"] ["c1" "c2" nil "c4"])))
    a1 b1 c1
    a2 b2 c2
    -- -- --
    -- -- c4
    nil
    

    请注意,这会将 colls 中的 nils 替换为 missing-value-fn 返回的值。

    【讨论】:

    • (not-every? nil? firsts) - 它不适用于 ['c1 'c2 nil 'c3] 之类的序列。
    • 尝试以下序列:['a1 'a2 nil] ['b1 'b2] ['c1 'c2 nil 'c4]。您可以通过在计算 firsts 之前检查 (every? empty? cols) 并将计算 firsts 本身替换为 (map first-or-val (conj colls c1)) 来修复它(请参阅我的答案)。
    【解决方案2】:

    这不是你需要的全部功能,而是一个稍微简化的版本,所以你可以明白这一点:

    (defn first-or-val [col missing]
      (if (empty? col)
        missing
        (first col)))
    
    (defn map-longest [f missing-value & cols]
      (loop [cols cols, ret '()]
        (cond (every? empty? cols) (reverse ret)
              :else (recur (map rest cols)
                           (conj ret (apply f (map #(first-or-val % missing-value)
                                                   cols)))))))
    

    我省略了懒惰,您可以使用delayforce 轻松添加。我还将missing-value-seq 更改为missing-value - 我相信这不是您用序列或生成器替换它的问题。

    例子:

    (print (apply str 
              (map-longest #(str %1 \space %2 \space %3 \newline) "--"
                           ['a1 'a2 'a3] ['b1 'b2] ['c1 'c2 'c3 'c4])))
    

    结果:

    a1 b1 c1
    a2 b2 c2
    a3 -- c3
    -- -- c4
    

    【讨论】:

      【解决方案3】:

      一个依赖于Clojure内部惰性的实现如下

      (defn map-longest
        [f missing-value & vs]
        (when (seq vs)
          (take
            (apply max (map count vs))
            (apply map
                   (fn[& ivs]
                     (apply f ivs))
                   (map
                     #(concat % (repeat missing-value))
                     vs)))))
      

      (map-longest (fn[a b c]
           (vector a b c))
         "--"
         ["a1" "a2" nil]
         ["b1" "b2"]
         ["c1" "c2" nil "c4"])
      

      返回

      (["a1" "b1" "c1"] ["a2" "b2" "c2"] [nil "--" nil] ["--" "--" "c4"])
      

      注意 nil 不等于缺少。

      【讨论】:

        猜你喜欢
        • 2013-11-26
        • 1970-01-01
        • 1970-01-01
        • 2014-11-20
        • 2014-10-31
        • 2014-01-21
        • 1970-01-01
        • 2014-03-12
        • 2012-01-27
        相关资源
        最近更新 更多