Замена некоторого элемента на следующий или предыдущий элемент в 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 является хвостом.
Обновленный список должен быть:
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
Не могли бы вы объяснить, в чем разница и как это работает?
2 ответа
Проблема только в последнем сопоставлении:
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
}
}
Работает на любую длину хз:
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
}