clojure 中交错的扩展

Posted

技术标签:

【中文标题】clojure 中交错的扩展【英文标题】:Extension of interleave in clojure 【发布时间】:2016-08-27 16:35:18 【问题描述】:

我想编写一个函数来交错两个给定的序列。该函数应该像这样工作:

user=> (ext-interl '(1 2 3 4 5 6 7 8) '(a b c))
(1 a 2 b 3 c 4 a 5 b 6 c 7 a 8 b)

该过程将在达到更长的序列时结束。

我的代码是:

(defn ext-interl [l1 l2]
 (lazy-seq
  (let [ls1 (seq l1) ls2 (seq l2)]
    (cond (and ls1 ls2)
     (cons (first ls1) (cons (first ls2) (ext-interl (rest ls1) (rest ls2))))
     ls1 ls1
     ls2 ls2))))

但是这段代码的运行方式如下:

 user=> (ext-interl '(1 2 3 4 5 6 7 8) '(a b c))
(1 a 2 b 3 c 4 5 6 7 8)

如何修复此代码?谢谢!

【问题讨论】:

你只是在练习吗?如果没有,你总是可以做(interleave [1 2 3 4 5 6 7 8] (cycle '[a b c])) @Shlomi 谢谢。我不知道函数循环。现在我可以弄清楚了。 【参考方案1】:

您的解决方案在您的第二个和第三个cond 子句中失败:当其中一个序列完全使用时,您只需将剩余的一个附加到末尾。您可能需要调用另一个函数,从一开始就将剩余的集合与另一个循环合并。例如:

(defn- ext-interleave' [orig-xs xs orig-ys ys]
  (let [xs (seq xs)
        ys (seq ys)]
    (cond
      (and xs ys)
      (concat
        [(first xs) (first ys)]
        (ext-interleave' orig-xs (rest xs) orig-ys (rest ys)))

      xs
      (interleave xs (cycle orig-ys))

      ys
      (interleave (cycle orig-xs) ys))))

(defn ext-interleave [xs ys]
  (ext-interleave' xs xs ys ys))

(ext-interleave [1 2 3 4 5 6 7 8] '[a b c])

此解决方案在第一个 cond 分支中大多是惰性的(它急切地消耗两个序列中的两个前项)。

如果您不需要惰性解决方案,我会使用现有函数来完成这项工作:

cycle + take 根据需要制作序列 核心interleave 将它们组合起来

解决方案:

(defn ext-interleave [xs ys]
  (let [l (max (count xs) (count ys))
        xs (take l (cycle xs))
        ys (take l (cycle ys))]
    (interleave xs ys)))
(ext-interleave [1 2 3 4 5 6 7 8] '[a b c])
;; => (1 a 2 b 3 c 4 a 5 b 6 c 7 a 8 b)

您甚至可以使其适用于任意数量的输入序列:

(defn ext-interleave' [& cols]
  (let [l (apply max (map count cols))
        cols (map #(take l (cycle %)) cols)]
    (apply interleave cols)))

(ext-interleave' [1 2 3 4 5 6 7 8] '[a b c] [:y :z])
;; => (1 a :y 2 b :z 3 c :y 4 a :z 5 b :y 6 c :z 7 a :y 8 b :z)

所提供的解决方案不会偷懒,因为count 将强制实现您的输入序列以计算其元素。

【讨论】:

请注意,如果ys 是较长的序列,则当前编写的ext-interleave' 颠倒了从xsys 中获取元素的顺序,从xs 第一个时开始用尽。这很容易通过在cond 的最后一个分支中交换interleave 的参数来解决。 @Piotrek Bzdyl。太感谢了!您的解决方案非常清晰易懂。但是,我想知道重复和循环之间有什么不同。由于我看了repeat和cycle的API,它们看起来是一样的。 我已经修正了我的错误。 cyclerepeat 之间的区别在于 repeat 重复单个元素,而 cycle 将从提供的序列中循环元素。【参考方案2】:

这里有三个版本:一个简单的,一个严格的;一个基于clojure.core 组合子的懒惰的;和一个基于相同组合器的惰性组合器,它接受任意数量的输入。

简单的严格方法

对惰性方法的健全性检查。

(defn interleave-longer-strict [xs ys]
  (take (* 2 (max (count xs) (count ys)))
    (interleave (cycle xs) (cycle ys))))

基于组合器的惰性方法

这是一个基于mapmapcattake-whileiterateinterleavecycle的懒惰版本:

(defn interleave-longer
  "Lazy version of

    (take (* 2 (max (count xs) (count ys)))
      (interleave (cycle xs) (cycle ys)))"
  [xs ys]
  (map (fn [_ e] e)
    (mapcat (fn [[xs ys]] [[xs ys] [xs ys]])
      (take-while (fn [[xs ys]] (or xs ys))
        (iterate (fn [[xs ys]] [(next xs) (next ys)])
          [xs ys])))
    (interleave (cycle xs) (cycle ys))))

为了证明它确实是惰性的(注意。(range) 永远不会返回 - 如果你真的通过 Long/MAX_VALUE 消耗它,它只会开始返回 clojure.lang.BigInts):

(take 30 (interleave-longer (range) (range 11)))
;= (0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 0 12 1 13 2 14 3)

(take 30 (interleave-longer (range 11) (range)))
;= (0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 0 11 1 12 2 13 3 14)

使用可变参数的基于组合器的惰性方法

最后,基于相同原语加上 applyrepeatcount(应用于可变参数序列以确定有多少输入)的惰性版本,它采用任意数量的输入:

(defn interleave-longest [& xss]
  (map (fn [e & _] e)
    (apply interleave (map cycle xss))
    (mapcat (fn [xss] (repeat (count xss) xss))
      (take-while (fn [xss] (some some? xss))
        (iterate (fn [xss] (mapv next xss))
          xss)))))

在 REPL:

(interleave-longest [:a :b :c :d] (range 11) '[x y z])
;= (:a 0 x :b 1 y :c 2 z :d 3 x :a 4 y :b 5 z :c 6 x :d 7 y :a 8 z :b 9 x :c 10 y)

(take 30 (interleave-longest [:a :b :c :d] (range) '[x y z]))
;= (:a 0 x :b 1 y :c 2 z :d 3 x :a 4 y :b 5 z :c 6 x :d 7 y :a 8 z :b 9 x)

【讨论】:

以上是关于clojure 中交错的扩展的主要内容,如果未能解决你的问题,请参考以下文章

Java扩展Nginx之二:编译nginx-clojure源码

精选版:用Java扩展Nginx(nginx-clojure 入门)

精选版:用Java扩展Nginx(nginx-clojure 入门)

精选版:用Java扩展Nginx(nginx-clojure 入门)

Java扩展Nginx之一:你好,nginx-clojure

Clojure Web 开发-- Ring 使用指南