用 Scala 列表中的下一个或上一个元素替换某些元素

Replacement some element with next or previous element in Scala List

了解 scala 中的一些函数后,我尝试修补列表中的元素:

val aList: List[List[Int]] = List(List(7,2,8,5,2),List(8,7,3,3,3),List(7,1,4,8,8))

我想做的是如果 8 的位置是头,则用右邻居元素替换 8,如果 8 是尾,则用左邻居元素替换。

更新后的 aList 应该是:

List[List[Int]] = List(List(7,2,2,5,2),List(7,7,3,3,3),List(7,1,4,4,4))

我试过以下代码:

def f(xs: List[Int]) = xs match {
  case x0 :: x1 :: x2 :: x3 :: x4 => List(x0,x1,x2,x3,x4)
  case 8 :: x1 :: x2 :: x3 :: x4 => List(x1,x1,x2,x3,x4)
  case x0 :: 8 :: x2 :: x3 :: x4 => List(x0,x0,x2,x3,x4)
  case x0 :: x1 :: 8 :: x3 :: x4 => List(x0,x1,x1,x3,x4)
  case x0 :: x1 :: x2 :: 8 :: x4 => List(x0,x1,x2,x2,x4)
  case x0 :: x1 :: x2 :: x3 :: 8 => List(x0,x1,x2,x3,x3)
}

aList.flatMap(f)

类型不匹配,因为类型是 Product with java.io.Serializable 但需要的是 scala.collection.GenTraversableOnce

你能解释一下有什么区别以及它是如何工作的吗?

问题出在最后一个匹配模式中:

case x0 :: x1 :: x2 :: x3 :: 8 => List(x0,x1,x2,x3,x3)

您将 8 放在列表尾部的位置,因此它必须具有 List[Int] 类型(或更一般地,如编译器告诉您的 GenTraversableOnce )。如果你有固定长度的内部列表,你应该改变你的模式以在最后有 :: Nil:

case 8 :: x1 :: x2 :: x3 :: x4 :: Nil => List(x1,x1,x2,x3,x4)
...
case x0 :: x1 :: x2 :: x3 :: 8 :: Nil => List(x0,x1,x2,x3,x3)

另一个选择是

case List(8, x1, x2, x3, x4) => List(x1,x1,x2,x3,x4)
...
case List(x0, x1, x2, x3, 8) => List(x0,x1,x2,x3,x3)

此外,您的第一个模式意味着其他模式将永远无法到达,它只是按原样保留列表。

如果您的内部列表不一定是固定大小的,您需要一个更通用的解决方案。如果是这样,请澄清。

此外,如果您想将 List[List[Int]] 映射到 List[List[Int]],您应该使用 .map(f) 而不是 flatMap

编辑

我注意到在你的示例中,在最后一个子列表中,你有两个 8 被左边的 4 替换了。如果你想实现这一点,你可以使你的函数递归并添加一个默认情况(当所有 8 被替换时)。

def f(xs: List[Int]) = xs match {
  case 8  :: x1 :: x2 :: x3 :: x4 :: Nil => f(List(x1,x1,x2,x3,x4))
  case x0 :: 8  :: x2 :: x3 :: x4 :: Nil => f(List(x0,x0,x2,x3,x4))
  case x0 :: x1 :: 8  :: x3 :: x4 :: Nil => f(List(x0,x1,x1,x3,x4))
  case x0 :: x1 :: x2 :: 8  :: x4 :: Nil => f(List(x0,x1,x2,x2,x4))
  case x0 :: x1 :: x2 :: x3 :: 8  :: Nil => f(List(x0,x1,x2,x3,x3))
  case _ => xs
}

但即使有了这些修复,f 仍会在开头有两个 8 的列表和其他一些边缘情况下循环。所以这是一个更通用的模式匹配解决方案:

def f(xs: List[Int]): List[Int] = {
  // if there are only 8s, there's nothing we can do
  if (xs.filter(_ != 8).isEmpty) xs
  else xs match {
    // 8 is the head => replace it with the right (non-8) neighbour and run recursion
    case 8 :: x :: tail if x != 8 => x :: f(x :: tail)
    // 8 is in the middle => replace it with the left (non-8) neighbour and run recursion
    case x :: 8 :: tail if x != 8 => x :: f(x :: tail)
    // here tail either starts with 8, or is empty
    case 8 :: tail => f(8 :: f(tail))
    case x :: tail => x :: f(tail)
    case _ => xs
  }
}

适用于任何长度的 xs:

  def f(xs: List[Int]) = {
    if (xs.length <= 1) xs else
    (for {
      i <- 0 until xs.length
    } yield {
      xs(i) match {
        case 8 => if (i == 0) xs(1) else xs(i - 1)
        case _ => xs(i)
      }
    }).toList
  }