Вот один из многих способов сделать это (используя Scala 2.13
s List#unfold
):
// val items = Seq(1, 2, 4, 3, 2, -1, 0, 6, 8)
items match {
case first :: _ :: _ => // If there are more than 2 items
List
.unfold(items.sliding(2).toList) { // We slid items to work on pairs of consecutive items
case Nil => // No more items to unfold
None // None signifies the end of the unfold
case rest @ Seq(a, b) :: _ => // We span based on the sign of a-b
Some(rest.span(x => (x.head - x.last).signum == (a-b).signum))
}
.map(_.map(_.last)) // back from slided pairs
match { case head :: rest => (first :: head) :: rest }
case _ => // If there is 0 or 1 item
items.map(List(_))
}
// List(List(1, 2, 4), List(3, 2, -1), List(0, 6, 8))
List.unfold
повторяется до тех пор, пока функция развертывания обеспечивает Some
. Он начинается с начального состояния, которое представляет собой список элементов, которые необходимо развернуть. На каждой итерации мы span
определяем состояние (оставшиеся элементы развертываются) на основе знака разницы заголовка двух элементов. Развернутые элементы являются заголовочными элементами, имеющими одинаковую монотонность, а развернутое состояние становится остальными элементами.
List#span
разбивает список на кортеж, первая часть которого содержит элементы, соответствующие примененному предикату, пока предикат не перестанет быть действительным. Вторая часть кортежа содержит остальные элементы. Который идеально соответствует ожидаемому типу возврата функции развертывания List.unfold
, который равен Option[(A, S)]
(в данном случае Option[(List[Int], List[Int])]
).
Int.signum
возвращает -1
, 0
или 1
в зависимости от знака целого числа, к которому он применен.
Обратите внимание, что первый элемент должен быть возвращен в результате, так как у него нет предка, определяющего его знак (match { case head :: rest => (first :: head) :: rest }
).
Чтобы применить сокращающую функцию (в данном случае сумму), мы можем отобразить конечный результат: .map(_.sum)