How roughen (as opposed to flatten) a list in a functional style?
For instance, I have a list (1 2 3 4 5 6 7 8 9 10 11)
, and want to roughen it by 3 elements (or another length) to get ((1 2 3) (4 5 6) (7开发者_如何学Python 8 9) (10 11))
. What pretty code could I use for this? Thanks.
List(1,2,3,4,5,6,7,8,9,10,11) grouped 3 toList
res0: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6),
List(7, 8, 9), List(10, 11))
Since you use the Clojure tag too...
There's a built-in function to do that in Clojure 1.2, also available in 1.1 in clojure.contrib.seq-utils.
(partition-all 3 [1 2 3 4 5 6 7 8 9 10 11])
; => ((1 2 3) (4 5 6) (7 8 9) (10 11))
See also partition
and partition-by
. Also note that partition
and partition-all
accept some optional arguments if you need something slightly different, see e.g. (doc partition)
at the REPL.
In Scala 2.8, List mixes in IterableLike which has the grouped method which returns an Iterator[List[T]], which in turn can be converted to List[List[T]].
List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11).grouped(3).toList
res3: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9), List(10, 11))
If you want a roughen method on List you can use an implicit conversion, something like:
scala> class RList[T](val l: List[T]) {def roughen(n: Int) = l.grouped(n).toList}
defined class RList
scala> implicit def list2rlist[T](l: List[T]) = new RList(l)
list2rlist: [T](l: List[T])RList[T]
scala> List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) roughen 3
res5: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9), List(10, 11))
def split[A](list : List[A], n : Int) : List[List[A]] = list match {
case List() => List()
case _ => (list take n) :: split(list drop n, n)
}
And another clojure version, written in more idiomatic clojure.
(defn roughen
[n coll]
(lazy-seq
(when-let [s (seq coll)]
(let [[l r] (split-at n s)]
(cons l (roughen n r))))))
Note, that split-at
traverses the input sequence twice. So you can replace the standard version with the following:
(defn split-at
[n coll]
(loop [n n, s coll, l []]
(if-not (zero? n)
(if-let [s (seq s)]
(recur (dec n) (rest s) (conj l (first s)))
[l nil])
[l s])))
(Of course one would use partition
and friends as already mentioned above.)
This is the best I could come up with:
def roughen(l:List[_],s:Int):List[_] ={
if (l.isEmpty) return Nil
val l2 = l.splitAt(s)
l2._1 :: roughen(l2._2,s)
}
val l = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
roughen(l,3)
//returns: List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9), List(10))
Here's a Clojure 1.0-compatible implementation of roughen:
(defn roughen
"Roughen sequence s by sub-grouping every n elements.
e.gn (roughen '(a b c d) 2) -> ((a b) (c d))"
[s n]
(loop [result () s s]
(cond (empty? s)
result
(< (count s) n)
(concat result (list s))
:default
(recur (concat result (list (take n s))) (drop n s)))))
user=> (roughen '(a b c d e f g) 2)
((a b) (c d) (e f) (g))
user=> (roughen '(a b c d e f) 2)
((a b) (c d) (e f))
user=> (roughen '(a b c d e f) 4)
((a b c d) (e f))
user=>
精彩评论