Вот первый проход, который немного сложнее с функциональной точки зрения, поскольку при накоплении результата необходимо поддерживать некоторое состояние.
fun <T> List<T?>.processList(action: (T) -> T): List<T> where T: Comparable<T> {
data class Accumulator(val resultList: MutableList<T> = mutableListOf(), var previousWasDupe: Boolean = false)
return this.asSequence()
.filterNotNull()
.windowed(2, partialWindows = true).fold(Accumulator()) { accum, value ->
if (accum.previousWasDupe) {
// skip this one that was already consumed in the dupe
accum.previousWasDupe = false
} else if (value.size < 2 || value[0] != value[1]) {
accum.resultList.add(value[0])
accum.previousWasDupe = false // already is false, you could delete this line, here for clarity
} else {
accum.resultList.add(action(value[0]))
accum.previousWasDupe = true
}
accum
}.resultList
}
для вашего теста:
val items = listOf("Q", null, "W", "E", null, "E", "E", "R", "R", "T")
println(items.processList { it + "gotcha" })
// prints "[Q, W, Egotcha, E, Rgotcha, T]"
Вы также можете использовать неизменяемый аккумулятор ( Мне не нравится, что он изменчив, но он будет работать лучше ), но нет смысла принимать этот удар, поскольку состояние является внутренним для функции.Будет ли это работать лучше в виде последовательности (как написано) или с копиями, зависит от размера списков, и трудно сказать без тестирования производительности.
Обратите внимание, что я также убедился, что элементы <T>
были Comparable
чтобы мы могли быть уверены, что ==
делает то, что мы ожидаем, в противном случае вы действительно не знаете, что получаете вещи, над которыми эта функция будет работать.
Для прикола неизменная версия:
fun <T> List<T?>.processListImmutable(action: (T) -> T): List<T> where T: Comparable<T> {
data class Accumulator(val resultList: List<T> = emptyList(), val previousWasDupe: Boolean = false)
return this.asSequence()
.filterNotNull()
.windowed(2, partialWindows = true).fold(Accumulator()) { accum, value ->
if (accum.previousWasDupe) {
Accumulator(accum.resultList, false) // could also be: accum.copy(previousWasDupe = false)
} else if (value.size < 2 || value[0] != value[1]) {
Accumulator(accum.resultList + value[0], false)
} else {
Accumulator(accum.resultList + action(value[0]), true)
}
}.resultList
}
Пограничный случай для проверки с решениями - это ввод:
val items = listOf("Q", null, "W", "E", null,
"E", "E", "R", "R", "T",
"Z", "Z", "Zgotcha") // <--- this is the trap
, который должен возвращать:
[Q, W, Egotcha, E, Rgotcha, T, Zgotcha, Zgotcha]
, а не:
[Q, W, Egotcha, E, Rgotcha, T, Zgotchagotcha]