Как перебрать список символов и сравнить разные элементы - PullRequest
0 голосов
/ 19 ноября 2018

У меня есть список строк, которые я хотел бы обработать, поэтому скажем,

val List<String?> = listOf("Q", NULL, "W", "E", NULL, "E", "E", "R", "R", "T") [sic]

Я хочу обработать это так, чтобы я мог применить предикат, где есть две совпадающие строки в смежных позициях, и вернуть список ненулевых строк.

Подпись метода расширения тогда будет

fun <T : Any> List<T?>.processList(action: (T) -> T): List<T>

Допустим, мой предикат на этот раз - добавить "gotcha" к первой строке в дубликате и удалить вторую, поэтому в этом случае я получу

ListOf("Q", "W", "Egotcha", "E", "Rgotcha", "T")

Я могу сделать это в старом стиле с помощью цикла while очень легко

грязная версия, существующая по этим направлениям

val nL : ArrayList<T> = ArrayList<T>()
var indexThis : Int = 0
while ( indexThis < intermediate.size-1 ) {
    if (intermediate[indexThis] != intermediate[indexThis + 1]) {
        nL.add(intermediate[indexThis]!!)
        indexThis += 1 }
    else {
        nL.add(processList(intermediate[indexThis]!!))
        indexThis += 2 }
}

Это далеко не готовая статья, но моя попытка прояснить процесс у меня в голове ...

но я изо всех сил пытаюсь начать делать это более функциональным способом

, чтобы я мог видеть, что я могу начать с

 this.filter {a -> a != null}

но тогда я не вижу, куда мне идти в качестве следующего шага? Тогда единственный способ, который я могу придумать, это forEachIndexed, но мои попытки сделать это кажутся очень запутанными. Я уверен, что я видел примеры людей, сравнивающих элементы в Списке на карте?

и мой последний ход мыслей шел по линии

.map{b -> b?.run{b ...  processList (b)}}

но это кажется очень неправильным

Кто-нибудь может указать мне правильное направление?

Ответы [ 2 ]

0 голосов
/ 19 ноября 2018

Вот первый проход, который немного сложнее с функциональной точки зрения, поскольку при накоплении результата необходимо поддерживать некоторое состояние.

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]
0 голосов
/ 19 ноября 2018

Попробуйте следующий код:

fun List<String?>.processList(action: (String) -> String): List<String> {
    var remove = false
    val dest = arrayListOf<String>()
    return filterNotNullTo(dest).mapIndexedNotNull { index, s ->
        if (remove) {
            remove = false
            null
        } else {
            if (index + 1 <= dest.lastIndex && s == dest[index + 1]) {
                remove = true
                action()
            } else {
                s
            }
        }
    }
}

val l = listOf("Q", null, "W", "E", null, "E", "E", "R", "R", "T")
val result = l.processList {"${it}gotcha"}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...