你能告诉 for 循环有条件地前进不止一步吗?

Can you tell a for-loop to conditionally advance by more than one step?

考虑以下代码:

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).

不,您不能更改 for-in 循环从循环内部迭代的方式。

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

您必须使用 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)
    }
}

您可以构建自定义 "skipping" 序列,使用提供的 Element -> Int? 谓词

  • if nil: 不要跳过下一次迭代,或者
  • if .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([=10=]) }) {
            for _ in 0..<skipNumIterations { n = iterator.next() }
        }
        return n
    }
}

为您申请的示例:

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

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

问题最后一段的答案是没有 - 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)
    }
}