我有一个序列列表 val as = Seq[A]
. A
看起来像这样:
import java.time.Instant
case class A(t: String, start: Instant, end: Instant)
现在我想有条件地合并 as
中的元素:每当两个后续的,即直接相邻的项 a1
和 a2
具有相同的 t
值时,它们应该合并为如下:
object A {
def merge(a1: A, a2: A): A = {
require(a1.t == a2.t)
A(a1.t, a1.start, a2.end)
}
}
请注意,永远不会在另一个项目上直接成功的项目不应合并,例如:
Seq(A("a", ...), A("a", ...), A("b", ...), ...) // -> merge the first two elements
然而:
Seq(A("a", ...), A("b", ...), A("a", ...), ...) // -> do not merge any elements
有一个similar question on SO,但它合并了多个列表,所以它不适用于我的情况 .
我的第一个方法:
as.zip(as.tail)
.map {
case (a1: A, a2: A) if (a1.t == a2.t) => merge(a1, a2)
case (a1: A, a2: A) => ???
}
但是有很多缺陷 . 除此之外,我不确定在第二种情况下该怎么做(即同时保持 a1
和 a2
),这对于应合并的多个后续元素也不起作用 .
我的直觉使我更加注重 foldLeft
:
as.foldLeft(???)(A.merge)
这解决了需要合并的多个后续元素的问题 . 但是,它会尝试合并我的 merge
实现无法实现的所有元素 .
我可以适应 merge()
,但我仍然不清楚如何:如果 a1.t == a2.t
,结果类型应该是新的 A
,否则它应该是'return' a1
和 a2
.
我对后一种想法的处理方法是将这些方法添加到类 A
中:
def merge(that: A): (A, Option[A]) =
if (this.t == that.t)
(A(t, this.start, that.end), None)
else
(this, Some(that))
但是在这里,我无法在序列 as
的 foldLeft()
调用中使用 merge()
的输出 .
根本问题在于两种方法:我应该如何处理有时(当 t
匹配时),我想在新序列中添加一个新的 A
,而在其他情况下,我需要添加两个元素 .
我对如何以函数式编程方式解决这个问题感到有点迷茫 . 我当然可以迭代 as
列表,存储应该在新数据结构中合并的那些,并再次生成它 . 有没有更好的办法?
2 回答
基于RoberMP的答案,如果你使用
foldRight
来构造List
,你可以使用与cons运算符匹配的模式,这应该比tail运算更有效:这是从右到左工作,以便我们可以在匹配中使用
::
但仍应具有预期的输出 .我认为foldLeft是正确的方法,类似于(未经测试):
请注意,这里有很多尾部操作,所以不知道Seq是否是这个用例的更好的集合