通过分区和折叠重写序列
映射顺序集合的最优雅和最简单的算法是什么,使得满足某些谓词的连续元素被折叠到另一个元素中,而那些不满足谓词的元素被 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
what is the most elegant and simple algorithm to map a sequential collection such that contiguous elements that satisfy some predicate are collapsed into another element, and those that do not satisfy the predicate are mapped 1:1 into another element?
here is an example:
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
given this input sequence:
val input = Seq(C(1), C(2), C(3), E(4), E(5), C(6), E(7), C(8), C(9))
the expected output is:
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)
here is one way of doing it, but i'm not sure this is particularly elegant:
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)
}
verify:
val output2 = split(input)
output2 == output // ok
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我会向 D 添加一个方便的方法,这样您就可以“添加”另一个 C 并获得一个新的 D。然后就可以很容易地使用一个简单的foldLeft 左右来构建一个新的Seq。
I would add a convenience method to D so you can "add" another C and get a new D back. Then it would be easy to use a simple foldLeft or so to build a new Seq.
@Landei 是的,确实,这看起来是一个很好的方法!
(当然,
IndexedSeq
对于lastOption
、dropRight
和追加来说更好。)@Landei yes, indeed, that looks like a good approach!
(Of course, an
IndexedSeq
is better forlastOption
,dropRight
and append.)