无法前进超过结束索引 Swift



在检查和删除符号时,字符串索引有问题。我该如何改进?

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

您正在修改您正在迭代的字符串,因此您的索引变得无效。相反,您可以添加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
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"有点胡言乱语,但起作用。但这与原始方法保持一致。

使用 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

您正在在循环内突变str,其最终索引正在变化,在这种情况下,它的原始值低于其原始值。您可以通过检查每次迭代的endIndex来修复代码:

var index = str.startIndex
while index < str.endIndex {
    ...
    //Before the closing curly brace of the while loop
    index = str.index(after: index)
}

我试图复制一个用户报告的崩溃,其中一个消息Can't advance past endIndex,但我无法做到。您的代码帮助我弄清楚此错误在以后版本的Swift中发生了变化。

您的相同代码将用Swift 4.x运行时库报告cannot increment beyond endIndex,而String index is out of bounds则以5.x。我不知道的更改的确切版本号。但我怀疑它是4.0.0和5.0.0 .-

最新更新