clojure 中交错的扩展
Extension of interleave in clojure
我想编写一个函数来交错两个给定的序列。该函数应该像这样工作:
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)
如何修复此代码?谢谢!
您的解决方案在第二个和第三个 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
将强制实现您的输入序列以计算其元素。
这里有三个版本:简单的,严格的;一个基于 clojure.core
组合器的懒惰的;和一个基于相同组合器的惰性组合器,它接受任意数量的输入。
简单严格的方法
对惰性方法的健全性检查。
(defn interleave-longer-strict [xs ys]
(take (* 2 (max (count xs) (count ys)))
(interleave (cycle xs) (cycle ys))))
基于组合器的惰性方法
这是一个基于 map
、mapcat
、take-while
、iterate
、interleave
和 cycle
的懒惰版本:
(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)
永远不会 returns – 如果你真的用完了 Long/MAX_VALUE
,它就会开始返回 clojure.lang.BigInt
s):
(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)
使用可变参数的基于组合器的惰性方法
最后,基于相同原语加上 apply
、repeat
和 count
的惰性版本(应用于可变参数序列以确定有多少输入)需要任意数量的输入:
(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)
我想编写一个函数来交错两个给定的序列。该函数应该像这样工作:
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)
如何修复此代码?谢谢!
您的解决方案在第二个和第三个 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
将强制实现您的输入序列以计算其元素。
这里有三个版本:简单的,严格的;一个基于 clojure.core
组合器的懒惰的;和一个基于相同组合器的惰性组合器,它接受任意数量的输入。
简单严格的方法
对惰性方法的健全性检查。
(defn interleave-longer-strict [xs ys]
(take (* 2 (max (count xs) (count ys)))
(interleave (cycle xs) (cycle ys))))
基于组合器的惰性方法
这是一个基于 map
、mapcat
、take-while
、iterate
、interleave
和 cycle
的懒惰版本:
(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)
永远不会 returns – 如果你真的用完了 Long/MAX_VALUE
,它就会开始返回 clojure.lang.BigInt
s):
(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)
使用可变参数的基于组合器的惰性方法
最后,基于相同原语加上 apply
、repeat
和 count
的惰性版本(应用于可变参数序列以确定有多少输入)需要任意数量的输入:
(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)