Algorithm 合并scala列表中的元素

Algorithm 合并scala列表中的元素,algorithm,scala,Algorithm,Scala,我正在尝试将以下Java代码段移植到Scala。它获取MyColor对象的列表,并合并彼此之间的增量中的所有对象。这似乎是一个可以用Scala的一些功能位优雅地解决的问题。有什么建议吗 List<MyColor> mergedColors = ...; MyColor lastColor = null; for(Color aColor : lotsOfColors) { if(lastColor != null) { if(lastColor.diff(aColor)

我正在尝试将以下Java代码段移植到Scala。它获取
MyColor
对象的列表,并合并彼此之间的增量中的所有对象。这似乎是一个可以用Scala的一些功能位优雅地解决的问题。有什么建议吗

List<MyColor> mergedColors = ...;
MyColor lastColor = null;
for(Color aColor : lotsOfColors) {
  if(lastColor != null) {
    if(lastColor.diff(aColor) < delta) {
      lastColor.merge(aColor);
      continue;
    }
  }
  lastColor = aColor;
  mergedColors.add(aColor);
}
列出合并的颜色=。。。;
MyColor lastColor=null;
用于(颜色A颜色:lotsOfColors){
if(lastColor!=null){
if(lastColor.diff(aColor)
我假设您在列表中以某种方式安排了颜色,使得颜色空间中的颜色“关闭”(即具有较低的
diff
值)在列表中相邻。然后我会使用折叠:

val unmergedColors: List[MyColor] = ...
val mergedColors = (Nil:List[MyColor] /: unmergedColors)( (list,c) => {
  list match {
    case oldc :: rest if (oldc.diff(c) < delta) => oldc.merge(c) :: rest
    case _ => c :: list
  }
}).reverse
val未合并颜色:列表[MyColor]=。。。
val mergedColors=(Nil:List[MyColor]/:unmergedColors)((List,c)=>{
列表匹配{
case oldc::rest if(oldc.diff(c)oldc.merge(c)::rest
case=>c::list
}
}).反向
这里,我假设
merge
被修改为返回前两个合并的新颜色(因此颜色是不可变的);否则,您将
oldc.merge(c);在第一种情况下列出

让我们看看这里发生了什么

我们从新颜色的空列表开始。然后,对于未合并列表中的每种颜色,我们有两种情况:

  • 合并列表有一个头部,头部的颜色在我们测试的颜色的增量范围内。在这种情况下,合并头部和新颜色,并将保存的列表与新头部一起传递
  • 否则,将新颜色添加到增长列表的前面并将其传递

最后,由于我们将这些操作用作堆栈操作,因此我们以颠倒列表来结束。

在我看来,这个问题可能会导致围绕到底是什么问题的各种问题。例如:

  • 解决方案在列表的初始顺序中是否应该保持不变
  • 在进行过程中,是否应针对合并或未合并的值进行差异化
但这里有一些有趣的东西使用递归(虽然不是尾部递归,但当然可以这样做),比如:

类型C=MyColor
类型Cs=列表[C]
def合并(val增量:双精度,差异:(C,C)=>双精度,颜色:Cs):Cs={
def_mergeHeadAndGTDiff(head:C,tail:Cs):Cs={
val(toMerge,rest)=尾跨(diff(head,_)Nil
案例x::xs=>\u mergeHeadAndGTDiff(newHead,xs)
})          
}
颜色匹配{
案例Nil=>Nil
案例x::xs=>\u mergeHeadAndGTDiff(x,xs)
}
}
解决方案如下所示:

  • 抓住头
  • 获取所有可以和头部合并的尾部元素,然后将它们(可以使用折叠)合并到一个新的头部中
  • 将新的头放在尾上,尾是由在第2步无法合并的所有东西组成的,然后在第1步将它们插回(在尾为空的情况下使用强制终止条款)

  • 我认为,作为一个
    ,这更有效。注意,我假设列表最初是由diff排序的,因为我使用的是
    span
    。如果将其替换为
    分区

    ,则不需要这样做。这里有另一个递归解决方案,它的优点是尾部递归(因此不存在堆栈溢出的机会),但缺点是会进行大量列表操作,因此可能是浪费。在最后调用reverse是将输出颜色恢复到输入顺序,但是如果您不关心顺序,则不需要这样做

    def processColors(colors: List[Color], delta: Double): List[Color] = {
      def process(in: List[Color], accum: List[Color]): List[Color] = in match {
          case x :: y :: ys if x.diff(y) < delta => process( x.merge(y) :: ys, accum )
          case x :: xs                           => process( xs, x :: accum )
          case Nil                               => accum
        }
    
      process(colors, Nil).reverse
    }
    
    def processColors(颜色:List[Color],delta:Double):List[Color]={
    def进程(in:List[Color],accum:List[Color]):List[Color]=匹配中{
    如果x.diff(y)进程(x.merge(y)::ys,累计),则情况x::y::ys
    案例x::xs=>process(xs,x::accum)
    案例无=>累计
    }
    过程(颜色,无)。反转
    }
    
    我会尝试折叠:

    def merge(lotsOfColor: List[MyColor], delta: Double): List[MyColor] =
      lotsOfColor.tail.foldLeft((List(lotsOfColor.head), lotsOfColor.head)) {
        case ((mergedColors, lastColor), aColor) if (lastColor diff aColor) < delta =>
          (mergedColors, lastColor merge aColor)
        case ((mergedColors, _), aColor) => (aColor :: mergedColors, aColor)
      }._1.reverse
    
    def合并(lotsOfColor:List[MyColor],delta:Double):List[MyColor]=
    lotsOfColor.tail.foldLeft((列表(lotsOfColor.head)、lotsOfColor.head)){
    案例((合并颜色,lastColor),A颜色)如果(lastColor差异A颜色)
    (合并颜色,lastColor合并颜色)
    大小写((合并颜色,u),aColor)=>(aColor::合并颜色,aColor)
    }1.相反
    
    或者,稍有不同,

    def merge(lotsOfColor: List[MyColor], delta: Double): List[MyColor] =
      lotsOfColor.tail.foldLeft((List(lotsOfColor.head), lotsOfColor.head)) {
        case ((mergedColors, lastColor), aColor) =>
          if ((lastColor diff aColor) < delta)
            (mergedColors, lastColor merge aColor)
          else
            (aColor :: mergedColors, aColor)
      }._1.reverse
    
    def合并(lotsOfColor:List[MyColor],delta:Double):List[MyColor]=
    lotsOfColor.tail.foldLeft((列表(lotsOfColor.head)、lotsOfColor.head)){
    大小写((合并颜色,lastColor),A颜色)=>
    if((lastColor diff aColor)
    在Scala中使用ListBuffer还有另一个很酷的技巧,以避免最后出现相反的情况:

    import scala.collection.mutable.ListBuffer
    def merge(lotsOfColor: List[MyColor], delta: Double): List[MyColor] =
      lotsOfColor.tail.foldLeft((ListBuffer(lotsOfColor.head), lotsOfColor.head)) {
        case ((mergedColors, lastColor), aColor) if (lastColor diff aColor) < delta =>
          (mergedColors, lastColor merge aColor)
        case ((mergedColors, _), aColor) => 
          mergedColors += aColor
          (mergedColors, aColor)
      }._1.toList
    
    导入scala.collection.mutable.ListBuffer
    def merge(lotsOfColor:List[MyColor],delta:Double):List[MyColor]=
    lotsOfColor.tail.foldLeft((ListBuffer(lotsOfColor.head)、lotsOfColor.head)){
    案例((合并颜色,lastColor),A颜色)如果(lastColor差异A颜色)
    (合并颜色,lastColor合并颜色)
    大小写((合并颜色),A颜色)=>
    合并颜色+=A颜色
    (合并颜色,aColor)
    }托利斯特
    
    您标记了这个“scale”而不是“scala”--我想您是指后者并修复了它。我认为ams下面的回答是在scala中使用尾部递归的一个极好的例子。您的代码中有一些问题困扰着我。您将颜色合并到
    lastColor
    ,但从未使用合并的
    lastColor
    。当差值大于增量时,算法要做的第一件事是将新颜色指定给
    lastColor
    ,然后
    import scala.collection.mutable.ListBuffer
    def merge(lotsOfColor: List[MyColor], delta: Double): List[MyColor] =
      lotsOfColor.tail.foldLeft((ListBuffer(lotsOfColor.head), lotsOfColor.head)) {
        case ((mergedColors, lastColor), aColor) if (lastColor diff aColor) < delta =>
          (mergedColors, lastColor merge aColor)
        case ((mergedColors, _), aColor) => 
          mergedColors += aColor
          (mergedColors, aColor)
      }._1.toList