在Scala中,当有时两个元素不应该变成一个元素时,如何折叠?
我有一个成对的列表,它们表示具有开始索引和结束索引的行。类型为(Int,Int)是两个整数的元组 例如,现在我想把任何相互接触的线条连接在一起 列表((1,5)、(6,10)、(12,20))应该转换为列表((1,10)、(12,20)),因为第(1,5)行接触第(6,10)行。只有接触线连接在一起形成一条线 我的第一个想法是在列表中使用foldleft,问题是foldleft需要将两个元素转换为一个元素,最终只输出一个(Int,Int) 第二个想法是我编写的递归解决方案,但这似乎不是最好或最简单的想法。我们获取两个元素,有时输出一个或两个元素的想法似乎应该应用于许多情况,就像foldleft所做的那样 有没有解决这个问题的通用方法、模式或库在Scala中,当有时两个元素不应该变成一个元素时,如何折叠?,scala,folding,fold,Scala,Folding,Fold,我有一个成对的列表,它们表示具有开始索引和结束索引的行。类型为(Int,Int)是两个整数的元组 例如,现在我想把任何相互接触的线条连接在一起 列表((1,5)、(6,10)、(12,20))应该转换为列表((1,10)、(12,20)),因为第(1,5)行接触第(6,10)行。只有接触线连接在一起形成一条线 我的第一个想法是在列表中使用foldleft,问题是foldleft需要将两个元素转换为一个元素,最终只输出一个(Int,Int) 第二个想法是我编写的递归解决方案,但这似乎不是最好或最简
一条直线位于一维平面上,它不是x,y点。(Int,Int)是一维平面上的起点和终点 澄清触摸的定义。列表中的所有行都不重叠,这是一个先决条件,即(1,3)和(2,4)不能存在于列表中,因为它们重叠 (1,3)、(4,5)可以存在于列表中,因为它们不重叠 (1,3),(4,5)确实接触,因为3和4之间的距离正好为1 对于问题((1,5)、(6,10)、(6,12)、(13,15))中给出的列表,这是一个无效列表,因为(6,10)、(6,12)重叠
请注意,这是我在写这个问题之前的工作代码,你可以看到它不太好。我只是在用我的知识构建递归函数,它构建一个结果并返回结果
private def joinAtoB(a: LineLike, b: LineLike): LineLike = {
newFunction(a.start, b.end)
}
private def joinTouchingLines(a: LineLike, b: LineLike): Option[LineLike] = {
if ((a.end + 1) == b.start)
Some(joinAtoB(a, b))
else
None
}
@tailrec
def joinLinesRec(list: List[LineLike], result: List[LineLike] = List[LineLike]())
:List[LineLike] = {
list match {
case Nil => result
case item :: Nil => item :: result
case first :: second :: rest => {
val joined = joinTouchingLines(first, second)
val prepend = joined match {
case None => List(first, second)
case Some(joinedItem) => List(joinedItem)
}
joinLinesRec(rest, prepend ::: result)
}
}
}
使用foldLeft执行此操作很简单,但如果在重叠或接触时取出规则的零件,则更简单:
def touching(fp: (Int, Int), sp: (Int, Int)) =
if (fp._2 >= sp._1) sys.error("Overlap " + fp + " and " + sp)
else if (fp._2 == sp._1 - 1) true
else false
然后,您可以决定何时只追加,何时扩展最后一对:
list.foldLeft(List[(Int,Int)]()){
(l, p) =>
if (l.isEmpty || !touching(l.head, p)) p :: l
else (l.head._1, p._2) :: l.tail
}.reverse
您可以使用模拟cons(
:
)的二进制函数使用foldRight
而不是foldLeft
,除非相邻元素接触:
def combine(x: (Int, Int), lst: List[(Int, Int)]) = (x, lst) match {
case (_, Nil) => List(x)
case ((a, b), (c, d) :: rest) => if (c == b+1)
(a, d) :: rest
else
(a, b) :: lst
}
所以两端都是包容的(因为(1,5)接触(6,10))?您想对多个可能的变体做什么,即((1,5)、(6,10)、(6,12)、(13,15))?您到底想最小化列表中剩余的元素数吗?不管怎样,看起来你们必须对列表进行排序,然后运行递归(或动态规划)算法。我认为第(1,3)行和第(2,4)行也很重要@菲尔,如果我错了,请纠正我。(如果所有行都是不相交的,这也足以得到唯一的解决方案。)
foldLeft
也可以返回列表。你只需要构造它。@pedrofura,那么也许foldLeft可以生成一个列表[(Int,Int)]?我不知道你是怎么想的。@Phil:这是经典的FP/逻辑编程练习,到目前为止,我已经用六种语言解决了:)但是你自己弄明白这一点的关键是看看与列表构造的联系,foldRight做得很好。例如,map
可以实现为一个foldRight
,在对元素应用一个函数后,它会在元素前面加上前缀。我很不情愿地赢得了这场胜利,我发现相对于命令式编程和可变状态,我很难理解这一点。也许某个图书馆会使这篇文章更容易写和理解。@larsmans(+1)优雅的回答,在类似的情况下帮助了我