Не может пройти мимо endIndex Swift - PullRequest
0 голосов
/ 05 марта 2019

Что-то не так с String Index при проверке и удалении символов.Как я могу улучшить его?

func romanToInt(_ s: String) -> Int {
    let romanDigits = ["I" : 1,
                       "V" : 5,
                       "X" : 10,
                       "L" : 50,
                       "C" : 100,
                       "D" : 500,
                       "M" : 1000]
    let romanSums = ["IV" : 4,
                     "IX" : 9,
                     "XL" : 40,
                     "XC" : 90,
                     "CD" : 400,
                     "CM" : 900]
    var sum = 0
    var str = s
    var charIndex = str.startIndex
    for index in str.indices {
        if index != str.index(before: str.endIndex) {
            charIndex = str.index(after: index)
        } else {
            charIndex = str.index(before: str.endIndex)
        }
        let chars = String(str[index]) + String(str[charIndex])
        if romanSums[chars] != nil {
            print(chars)
            str.remove(at: charIndex)
            sum += romanSums[chars]!
            print(sum)
        } else {
            let char = String(str[index])
            print(char)
            sum += romanDigits[char]!
            print(sum)
        }
        print(str)
    }

    return sum
}

let check = romanToInt("MCMXCIV")

ЖУРНАЛ КОНСОЛИ:

M
1000
MCMXCIV
CM
1900
MCXCIV
XC
1990
MCXIV
IV
1994
MCXI
Fatal error: Can't advance past endIndex

Ответы [ 5 ]

2 голосов
/ 05 марта 2019

Вы изменяете строку, по которой вы перебираете, поэтому ваши индексы становятся недействительными. Вместо этого вы можете добавить логическое значение skipChar, которое говорит, что вы уже обработали следующий символ, а затем пропустить этот символ, выполнив continue:

func romanToInt(_ s: String) -> Int {
    let romanDigits = ["I" : 1,
                       "V" : 5,
                       "X" : 10,
                       "L" : 50,
                       "C" : 100,
                       "D" : 500,
                       "M" : 1000]
    let romanSums = ["IV" : 4,
                     "IX" : 9,
                     "XL" : 40,
                     "XC" : 90,
                     "CD" : 400,
                     "CM" : 900]
    var sum = 0
    var str = s
    var charIndex = str.startIndex
    var skipChar = false

    for index in str.indices {
        if skipChar {
            skipChar = false
            continue
        }
        if index != str.index(before: str.endIndex) {
            charIndex = str.index(after: index)
        } else {
            charIndex = str.index(before: str.endIndex)
        }
        let chars = String(str[index]) + String(str[charIndex])
        if romanSums[chars] != nil {
            print(chars)
            skipChar = true
            sum += romanSums[chars]!
            print(sum)
        } else {
            let char = String(str[index])
            print(char)
            sum += romanDigits[char]!
            print(sum)
        }
        print(str)
    }

    return sum
}

let check = romanToInt("MCMXCIV")
print(check)
1994
2 голосов
/ 05 марта 2019
for index in str.indices {
    ...
        str.remove(at: charIndex)

Недопустимо изменять строку во время ее итерации. str.indices извлекается здесь один раз и становится недействительным после того, как вы изменили основную строку.

Я уверен, что будет много реализаций этого, потому что это небольшая, забавная проблема, которая привлекает реализации. Так почему не? Это просто крик рекурсии для меня.

let romanDigits: [Substring: Int] = ["I" : 1,
                                     "V" : 5,
                                     "X" : 10,
                                     "L" : 50,
                                     "C" : 100,
                                     "D" : 500,
                                     "M" : 1000]
let romanSums: [Substring: Int] = ["IV" : 4,
                                   "IX" : 9,
                                   "XL" : 40,
                                   "XC" : 90,
                                   "CD" : 400,
                                   "CM" : 900]

func romanToInt<S: StringProtocol>(_ s: S) -> Int
    where S.SubSequence == Substring {

    if s.isEmpty { return 0 }

    if let value = romanSums[s.prefix(2)] {
        return value + romanToInt(s.dropFirst(2))
    } else if let value = romanDigits[s.prefix(1)] {
        return value + romanToInt(s.dropFirst(1))
    } else {
        fatalError("Invalid string")
    }
}

let check = romanToInt("MCMXCIV")

Конечно, это на самом деле не проверяет правильность последовательностей, так что это своего рода мусор. «IIIIXXIII» - своего рода бред, но это работает. Но это соответствует первоначальному подходу.

1 голос
/ 06 марта 2019

Используйте reduce, чтобы сделать это здесь:

 func romanToInt(_ s: String) -> Int {
if s.isEmpty {return 0}

let romanDigits = ["I" : 1,
                   "V" : 5,
                   "X" : 10,
                   "L" : 50,
                   "C" : 100,
                   "D" : 500,
                   "M" : 1000]

let romanSums = ["IV" : 4,
                 "IX" : 9,
                 "XL" : 40,
                 "XC" : 90,
                 "CD" : 400,
                 "CM" : 900]
    return s.dropFirst().reduce((s.first!, romanDigits["\(s.first!)"]!)){

         return   ( $1,   //current char

                    $0.1 +   //previous sum

      (romanSums["\($0.0)\($1)"]     //add double value

     ?? ((romanDigits["\($1)"]!). +  romanDigits["\($0.0)"]!))  //or single value  and add duplicated

        -  romanDigits["\($0.0)"]!) // minus duplicated

     }.1

   }

   print(romanToInt("MCMXCIV")). //1994
0 голосов
/ 08 мая 2019

Я пытался воспроизвести сбой, о котором сообщал один из моих пользователей с тем же сообщением Can't advance past endIndex, но я не смог этого сделать.Ваш код помог мне понять, что эта ошибка изменилась в более поздних версиях swift.

Ваш же код сообщал бы cannot increment beyond endIndex с библиотеками среды выполнения swift 4.x и String index is out of bounds с 5.x.Точные номера версий для изменений я не знаю.Но я подозреваю, что это 4.0.0 и 5.0.0 .-

0 голосов
/ 05 марта 2019

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

var index = str.startIndex
while index < str.endIndex {
    ...
    //Before the closing curly brace of the while loop
    index = str.index(after: index)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...