将一些元素替换为Scala列表中的下一个或上一个元素

时间:2016-11-02 00:43:41

标签: scala collections serializable flatmap

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

<div>

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

更新后的aList应为:

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

我尝试过以下代码:

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

请您解释一下有什么区别以及它是如何工作的?

2 个答案:

答案 0 :(得分:1)

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

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 s和一些其他边缘情况。所以这是一个更通用的模式匹配解决方案:

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
  }
}

答案 1 :(得分:1)

适用于任何长度的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
  }