【问题标题】:How to dynamicly change conditions in loop for Kotlin如何在循环中为 Kotlin 动态更改条件
【发布时间】:2021-07-26 11:55:00
【问题描述】:

我的任务是从两个字符串中删除字母,如果有相等并保持在相同的位置。 我这样做并且总是让索引超出界限异常。无法理解如何修复索引并动态制作我的高板。

   var newSecret = StringBuilder(secret)
    var newGuess = StringBuilder(guess)    

 for (i in newSecret.indices){
            if (newSecret[i]==newGuess[i]){
                newSecret.deleteCharAt(i)
                newGuess.deleteCharAt(i)
                rightPosition++
    
            }

【问题讨论】:

    标签: string loops for-loop kotlin conditional-statements


    【解决方案1】:

    在遍历它时不更改任何类型的集合是有原因的,即使它不是Collection 的一部分,StringBuilder 显然是一种集合,因为它具有 append 和 deleteAt 函数。

    如果你有一个字符串0123456789,0 在索引 0 中,5 在索引 5 中。但是如果我们删除假设 3,那么它之后的所有其他内容现在都有不同的索引。我们有012456789,如果你数一下,你可以看到 5 不会有索引 4,以此类推。这意味着您的程序不仅会删除它不应该删除的东西,而且如果它想删除索引 9,那么它不再存在,因此,索引超出范围。

    可能还有其他方法可以做到这一点,这些方法更有效且易于理解,但我已尝试保持与示例代码中相同的想法。

    val newSecret = StringBuilder(secret)
    val newGuess = StringBuilder(guess)
    
    secret.withIndex()
        .filter { guess.length >= it.index + 1 //in case that guess is shorter than secret (if you know that they are the same length, this part of the predicate can be removed
                && it.value == guess[it.index] }
        .map { it.index }
        .reversed() //we want to delete from end to start, to avoid what we discussed above
        .forEach {
            newSecret.deleteCharAt(it)
            newGuess.deleteCharAt(it)
        }
    

    在上述过程中,如果secret和guess是:

     val secret = "bar1bar2x5"
     val guess = "foo1foo3y5"
    

    那么 newSecret 和 newGuess 将是:

    newSecret -> barbar2x
    newGuess -> foofoo3y
    

    【讨论】:

      【解决方案2】:

      如果您使用 IDE 调试代码或仅打印语句,您将很快意识到索引 i 正在增加,但 newSecretnewGuess 的长度会随着每次 deleteCharAt 调用而减少。因此newSecret[i] 会给你Index OutofBoundException。

      @gidds 在评论中给出了很好的解释:newSecret.indices 在循环之前只被评估一次——删除字符时不会重新评估范围,这是异常的原因

      for 循环可能不适合这个用例,你可以试试 while 循环。您必须使用两个字符串的长度检查索引,并仅在需要时增加 i

        val newSecret = StringBuilder("abcdef")
        val newGuess = StringBuilder("abddeg")
      
        var i = 0
        while(i<newSecret.length && i < newGuess.length) {
          if (newSecret[i] == newGuess[i]) {
            newSecret.deleteCharAt(i)
            newGuess.deleteCharAt(i)
          }
          else i++
        }
      

      【讨论】:

      • 可能值得明确指出,在问题中,newSecret.indices 在循环之前只被评估一次——当字符被删除时,范围重新评估,这是异常的原因。
      【解决方案3】:

      老实说,我认为您从每个副本的完整副本开始,然后尝试删除内容并跟踪新状态,这会使这变得更加困难。如果你可以从头开始构建字符串,丢弃你不需要的东西,那就更简单了:

      val newSecret = StringBuilder()
      val newGuess = StringBuilder()
      secret.zip(guess).forEach { (a, b) ->
          if (a != b) {
              newSecret.append(a)
              newGuess.append(b)
          }
      }
      

      zip 接受两个可迭代对象并成对地为您提供它们的元素,每个元素一个(就像将两组牙齿连接在一起的拉链)。因此,它非常适合您比较相同位置的项目的情况。

      我认为当您想要 String 结果时,这是最易读的方法,一般情况是

      secret.zip(guess).filterNot { (a, b) -> a == b }.unzip()
      

      zipping 为您提供一对列表,unzip 将其转换为一对列表。你也可以在这里使用它,但这意味着你必须在每个列表上调用joinToString(""),然后将Pair 的每个组件分配给一个变量......我认为上面的StringBuilder 版本在这种情况下更好

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-04-14
        • 2017-07-08
        • 2017-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多