你能告诉 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 循环的多次迭代(例如 continueMany
或continue 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)
}
}
考虑以下代码:
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 循环的多次迭代(例如 continueMany
或continue 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)
}
}