Как отформатировать строку с целыми и специальными символами - PullRequest
0 голосов
/ 25 января 2019

У меня есть строка с результатами спортсмена на соревнованиях по прыжкам в высоту.Здесь + соответствует успешной попытке,% неудачной - пропущено.Высота и соответствующие попытки разделены пробелом.Мне нужно прочитать строку и вернуть максимальную принятую высоту, или -1, если ее нет, или формат строки нарушен.Например:

  1. "220 + 224 %+ 228 %- 230 + 232 %%- 234 %".// result in 230
  2. "226 +" // result is 226
  3. "???" // result is -1

Я пытался использовать регулярные выражения, ноне достиг большого успеха.

fun bestHighJump(jumps: String): Int {
    var result = 0
    val jumpsSplitted = jumps.split(" ")
    val regStr = Regex("""[.+\d]""")
    for (item in jumpsSplitted) {
        if (regStr.containsMatchIn(item)) result += item
    }
    //Don't know what to do next
}

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

Ответы [ 3 ]

0 голосов
/ 25 января 2019

Сначала вам нужно обрезать и очистить аргумент jumps из нескольких соседних пробелов.Затем, разделив его, создайте 2 списка:

1-й содержит высоты ивторой содержит попыток .

Необходимо проверить, что 1-й список содержит действительных целых чисел и 2 списка одинакового размера .Затем, удалив все вхождения % и - в каждом элементе 2-го списка,Вы получаете индекс последнего + предмета.Этот index используется для получения соответствующей высоты из 1-го списка.

fun bestHighJump(jumps: String): Int {
    val jumpsSplitted = jumps.trim().replace(Regex("\\s\\s+"), " ").split(" ")
    if (jumpsSplitted.size < 2 || jumpsSplitted.size % 2 == 1)
        return -1

    val heights = jumpsSplitted.filterIndexed { i, _ ->  (i % 2 == 0)}.mapNotNull { it.toIntOrNull() }
    val attempts = jumpsSplitted.filterIndexed { i, _ ->  (i % 2 == 1)}
    if (heights.size != attempts.size)
        return -1

    val index = attempts
            .map { it.replace("%", "").replace("-", "") }
            .indexOfLast { it == "+" }

    return if (index == -1) -1 else heights[index]
}

Это

val jumps1 = "220 + 224 %+ 228 %- 230 + 232 %%- 234 %"
println(bestHighJump(jumps1))

будет печатать 230Это

val jumps2 = "226 +"
println(bestHighJump(jumps2))

напечатает 226Это

val jumps3 = "???"
println(bestHighJump(jumps3))

напечатает -1

0 голосов
/ 26 января 2019

Использование регулярных выражений - хорошая идея.Группы становятся особенно удобными здесь.

([\\d]{1,3})([ +\\-%]*)

Регулярное выражение будет сопоставлять любое 1-3-значное число в своей первой группе и результат этой попытки во второй группе.

Вы получаете Sequence<MatchResult> с findAll, а затем вы фильтруете и отображаете значения, представляющие интерес.

В конце вы возвращаете максимум успешных попыток или -1, если входная строка была недействительной или не содержала успешный переход.

fun bestHighJump(jumps: String): Int {
    val attempts = Regex("([\\d]{1,3})([ +\\-%]*)").findAll(jumps)

    val list = attempts.filter {
        it.groups[2]?.value?.let {
            '+' in it
        } == true
    }.map {
        it.groups[1]?.value
    }.mapNotNull { it?.toInt() }.toList()

    return if (list.isEmpty()) {
        -1
    } else {
        list.max()!!
    }
}

Обратите внимание, что в котлине MatchGroupCollection первая группа будет иметь индекс 1, а вторая - индекс 2, поскольку обе группы с индексом 0 являются объединенными группами.

0 голосов
/ 25 января 2019

(переписано с учетом уточняемого вопроса.)

Насколько я понимаю, строка должна содержать целые числа, чередующиеся с символами, и нам нужно наибольшее целое число, за которым следуетпо "+".

Я бы подошел к этому, разделив строку на слова, а затем посмотрев на пар слов.Мы можем сделать это с помощью функции zipWithNext(), которая выдает список всех соседних пар.Затем мы можем filter() выбрать только те, в которых второе слово равно "+", mapNotNull(), чтобы преобразовать первое слово такой пары в Int, где это возможно (игнорируя null s из тех, которые неt действительные целые числа) и возьмите max() из этих чисел.(Если их нет, max() возвращает null, поэтому мы можем вместо этого использовать оператор Элвиса для замены -1.)

fun String.bestHighJump()
    = split(" ")
      .zipWithNext()
      .filter{ it.second == "+" }
      .mapNotNull{ it.first.toIntOrNull() }
      .max() ?: -1

(я сделал эту функцию расширения для String, в основном потому, что кажется, что он хорошо подходит, он также избегает необходимости объявлять и затем использовать параметр. Но нормальная функция будет работать почти так же.)

Чтобы сделать это более идиоматичным, он бывероятно, лучше удалить оператора Элвиса и вернуть null напрямую, если не было совпадений;это делает ситуацию более очевидной для вызывающей стороны, которая может затем решить, как обрабатывать этот случай.

Поскольку вы смотрите на регулярные выражения, вот альтернативное решение, использующее одно из них:

fun String.bestHighJump()
    = Regex("""([0-9]+) [+]\B""")
      .findAll(this)
      .map{ it.groupValues[1].toInt() }
      .max() ?: -1

Это на одну строку короче, но я думаю, что это намного менее понятно.По моему опыту, регулярные выражения часто трудно понять правильно, отладить и поддерживать, поэтому я предпочитаю другие подходы.(Для сравнения, первая версия сработала впервые, в то время как версия регулярного выражения потребовала много попыток!)

Здесь регулярное выражение соответствует одному успешному результату (одна или несколько цифр, за которыми следует пробел, + изатем граница слова. (Последнее необходимо, потому что мы не знаем, является ли этот результат последним в строке, или за ним следуют другие. Регулярные выражения совпадают жадно, поэтому нам также не нужен один в начале.)

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

(Вопрос по-прежнему не ясен о том, как обрабатывать показатели успеха с несколькими символами. Я предполагаю, что мы хотим толькоподсчитывать результаты, где индикатором успеха является только один символ «+». Если мы также должны включить случаи, когда среди «%» и / или других символов есть «+», то оба функцииионы могут быть настроены для этого.Но опять же, регулярное выражение было бы сложнее сделать.)

Что касается того, где учить подобные вещи, я предполагаю, что вы уже знаете о Kotlin docs .(И я бы порекомендовал книгу Kotlin In Action для изучения языка.) Существуют разные подходы к обработке строк, в зависимости от ваших потребностей, поэтому их сложнее рекомендовать - но основные принципы не конкретныв Котлин, так что, вероятно, есть много мест, где можно посмотреть.И если у вас есть более сложный случай, вы всегда можете опубликовать его как еще один вопрос здесь!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...