用 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
}
了解 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
}