开发者

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=> 
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜