views:

307

answers:

7

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 8 9) (10 11)). What pretty code could I use for this? Thanks.

+1  A: 
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)
}
Dario
+14  A: 
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))
Thomas Jung
+1 Nice! ......
Dario
Good! have any clojure code ?
+3  A: 

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))
Don Mackenzie
Oops, didn't see Thomas's answer.
Don Mackenzie
All answer is very nice ! but the grouped name is bad,the roughen is more general ,like against flatten :P ,maybe roughen recieve a function that roughen by it,such length,regex...,will more better
But roughen would imply (to me at least) that the lists weren't the same length, like a ragged array. I think partition is used in other languages/libraries for the same operation.
pdbartlett
+1  A: 

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))
dbyrne
+15  A: 

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.

Michał Marczyk
I'm frequently amazed by the breadth of functionality in Clojure's libraries... but wish there was a clever way to find the ones that you want quickly! (of course asking the superstars on stackoverflow is always an option :-) )
mikera
See http://clojure.org/cheatsheet - partition is right there in "Nested seqs".
Jouni K. Seppänen
+1  A: 

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=> 
Greg Harman
+1  A: 

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.)

kotarak