2011-03-03 23 views
1

什麼是映射順序集合的最優雅和簡單的算法,以便滿足某些謂詞的連續元素被摺疊到另一個元素中,而那些不滿足謂詞的元素被映射爲1:1到另一個元素?通過分區和摺疊來重寫序列

這裏有一個例子:

sealed trait A // say the input elements are of this type 
sealed trait B // say the output elements are of this type 
case class C(i: Int) extends A // these are the input elements satisfying the predicate 
case class D(s: C*) extends B // they should be collapsed into this 
case class E(i: Int) extends A with B // these are input elems that are left as such 
給該輸入序列

val input = Seq(C(1), C(2), C(3), E(4), E(5), C(6), E(7), C(8), C(9)) 

預期輸出是:

val output = Seq(D(C(1), C(2), C(3)), E(4), E(5), D(C(6)), E(7), D(C(8), C(9))) 
//    ---------------  - -  -  -  -------- 
// the dashes indicate how the sequence is regrouped (collapsed) 

這裏是做這件事的一種方式,但我我不確定這是否特別優雅:

def split(xs: Seq[A]): Seq[B] = split1(Seq.empty[B], true, xs) 
@annotation.tailrec def split1(done: Seq[B], test: Boolean, rem: Seq[A]) : Seq[B] = { 
    val (pre, post) = rem.span { case _: C => test; case _ => !test } 
    val add = if(test) { 
     D(pre.collect({ case x: C => x }): _*) :: Nil 
    } else { 
     pre.collect({ case x: E => x }) 
    } 
    val done2 = done ++ add 
    if(post.isEmpty) done2 else split1(done2, !test, post) 
} 

驗證:

val output2 = split(input) 
output2 == output // ok 

回答

1

我想補充一個便捷方法d,所以你可以在「添加」另一個C和獲得新的d回來。那麼使用一個簡單的foldLeft就可以輕鬆地構建一個新的Seq。

0

@Landei是的,的確,這看起來很不錯!

val output2 = input.foldLeft(Seq.empty[B]) { 
    case (res, c: C) => res.lastOption match { 
    case Some(D(x @ _*)) => res.dropRight(1) :+ D((x :+ c): _*) 
    case _ => res :+ D(c) 
    } 
    case (res, e: E) => res :+ e 
} 

output2 == output // ok 

(當然,一個IndexedSeqlastOptiondropRight和追加更好。)

+0

你可以用'init'代替dropRight的'(1)' –