【问题标题】:Can you tell a for-loop to conditionally advance by more than one step?你能告诉一个 for 循环有条件地前进不止一步吗?
【发布时间】:2018-08-19 01:53:49
【问题描述】:

考虑以下代码:

let value = "ABCDE"

for letter in value {
    print(letter)
}

打印出来:

A
B
C
D
E

现在考虑假设代码,当它找到“2”时,它会在循环中向前跳过 2。这是显示我要问的伪代码...

let value = "AB2DE"

for letter in value {

    if letter == "2" {
        continue 2 <-- Does Swift support something similar to this?
    }

    print(letter)
}

以上将使输出看起来像这样:

A
B
E

注意:当然,我可以通过管理跳过计数器轻松实现这一点,然后使用continue,在我进行时递减计数器,或者,如果源支持下标,正如其他人建议的那样,我可以使用我在其中管理自己的索引的while 循环。有无数种方法可以解决这个假设的问题,但这不是我的问题的意图。

我的问题是专门找出语言本身是否支持一次跳过多个 for 循环的迭代(例如 continueManycontinue x)。

【问题讨论】:

  • 这可能对你有帮助:stackoverflow.com/questions/37170203/…
  • 那是使用固定间隔。我需要一些有条件的东西。我认为使用我自己的索引的“while”循环是可行的方法。
  • 您是否期望该语言为您将字符串解码为整数? :)
  • 另外,是否应该为剩余的字符保留新解码的 int?还是应该只使用一次?
  • @MarqueIV:您将遍历集合 indices, 并有条件地将其推进一个或多个。与下面 rmaddy 建议的 while 循环基本相同,只是它类似于传统的 for 循环,迭代变量是主体内的 constant,其范围仅限于循环。

标签: swift for-loop swift4 continue


【解决方案1】:

不,您无法更改 for-in 循环在循环内的迭代方式。

在这种情况下,带有您自己的索引计数器的while 循环可能是最简单的解决方案。尽管您可以在for-in 循环中使用sequence(first:next:)

【讨论】:

  • 只是出于好奇,您能详细说明一下sequence(first:next:)吗?
  • 在您的问题下方的第一条评论中@Cristik 提供的链接中已包含答案。当然,您可以查看它的文档。
  • 谢谢!没注意到,当我看到这个问题问的是不同的东西时,我跳过了它。
【解决方案2】:

您必须使用while 循环。由于 Swift 中字符串索引改进的工作方式,与许多其他语言相比,这有点复杂:

let value = "AB2DE"

var index: String.Index? = value.startIndex
while let charIndex = index, charIndex < value.endIndex {
    let letter = value[charIndex]

    if let digit = Int(String(letter)) {
        index = value.index(charIndex, offsetBy: digit, limitedBy: value.endIndex)
    } else {
        index = value.index(after: charIndex)
        print(letter)
    }
}

【讨论】:

  • 是的,这是解决演示问题的一种方法,但我更多的是询问该语言本身支持什么。这就是为什么我把警告放在首位。开始认为答案是“Swift 不提供跳过一次以上迭代的原生支持”。
【解决方案3】:

问题最后一段的答案是 - Swift 没有类似continue N 的声明。

不过,您可以使用其他一些语言结构来获得类似的结果。如果要跳过的元素是其中之一,则使用reduce 携带数字:

_ = value.reduce(0) { skip, char in
    // skip while we need to
    guard skip <= 0 else { return skip - 1 }
    // attempt to get a new skip count
    let skip = Int(String(char)) ?? 0
    if skip <= 0 {
        // do the actual work
        print(char)
    }
    // decrement the skip count
    return skip - 1
}

或者,一种更重要的方法是使用skip 方法扩展Iterator,并使用迭代器遍历集合(集合上的for 循环只是遍历的糖语法一个迭代器):

extension IteratorProtocol {
    @discardableResult
    mutating func skip(_ n: Int) -> Element? {
        guard n > 0 else { return next() }
        return skip(n-1)
    }
}

var it = value.makeIterator()
while let char = it.next() {
    if let n = Int(String(char)) {
        it.skip(n)
    } else {
        print(char)
    }
}

【讨论】:

    【解决方案4】:

    您可以使用提供的Element -&gt; Int? 谓词来构建您的自定义“跳过”序列

    • 如果nil:不要跳过下一次迭代,或者
    • 如果.some(number):跳过下一个number 迭代

    然后使用此构造迭代您的序列 (value) 并有条件地向前跳转。

    例如:

    struct SkippingSequence<T: Sequence>: IteratorProtocol, Sequence {
        var iterator: T.Iterator
        let skipPredicate: (T.Element) -> Int?
        // skipPredicate:
        // - nil means no skip,
        // - .some(number) means to skip next 'number' of iterations.
        init(_ collection: T, _ skipPredicate: @escaping (T.Element) -> Int?) {
            self.iterator = collection.makeIterator()
            self.skipPredicate = skipPredicate
        }
    
        mutating func next() -> T.Element? {
            var n = iterator.next()
            if let skipNumIterations = n.flatMap({ skipPredicate($0) }) {
                for _ in 0..<skipNumIterations { n = iterator.next() }
            }
            return n
        }
    }
    

    为你申请的例子:

    let collection = "AB2DEF3HIJK"
    for letter in SkippingSequence(collection, { Int(String($0)) }) {
        print(letter)
    } /* A
         B
         E (skipped 2, D)
         F
         J (skipped 3, H, I)
         K */
    

    skipPredicate 命名并不能真正解释其用途,可以重命名为具有更好语义的名称。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-16
      • 2011-11-05
      • 2020-06-03
      • 1970-01-01
      • 2020-01-07
      • 2015-06-28
      相关资源
      最近更新 更多