无法超越endIndex Swift

时间:2019-03-05 20:16:22

标签: swift string char

检查和删除符号时字符串索引出现错误。我该如何改善?

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 个答案:

答案 0 :(得分:2)

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 :(得分:2)

您正在修改要迭代的字符串,因此索引无效。相反,您可以添加一个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 :(得分:1)

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

答案 3 :(得分:0)

您正在循环内对str进行突变,其结束索引将发生变化,在这种情况下,它的值低于其原始值。您可以使用while循环,通过检查每次迭代的循环次数是否未超过endIndex来修正代码:

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

答案 4 :(得分:0)

我试图重现我的用户报告的崩溃,并显示相同的消息Can't advance past endIndex,但我无法做到。您的代码帮助我弄清楚了该错误在swift的更高版本中已更改。

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