我想知道关于Swift 2.2中非+1增加迭代器循环的最佳解决方案,因为旧样式现已弃用:
for (var i = 0; i < planets.count; i += 1) {
let string = planets[i]
if (string == "Venus" || string == "Saturn") {
i += 2
}
}
我的第一个解决方案是使用有效的while
var i = 0
while i < planets.count {
let string = planets[i]
if (string == "Venus" || string == "Saturn") {
i += 2
}
i += 1
}
但在整个代码中多次使用它并不是很方便。所以我创建了一个似乎也可以工作的数组扩展:
extension Array {
func loopIncrement(callback: (inout i: Int) -> ()) {
var index: Int = 0
while index < self.count {
callback(i: &index)
index += 1
}
}
func loopDecrement(callback: (inout i: Int) -> ()) {
var index: Int = self.count - 1
while index >= 0 {
callback(i: &index)
index -= 1
}
}
}
像这样使用它:
planets.loopIncrement { (i) in
let string = planets[i]
if (string == "Venus" || string == "Saturn") {
i += 2
}
}
planets.loopDecrement { (i) in
let string = planets[i]
if (string == "Venus" || string == "Saturn") {
i -= 2
}
}
现在这很好,但我确信有更好的方法可以解决这个问题。
所以我们在这里开始讨论。非常感谢任何帮助!
如果你想看一下,这是Playground文件:NonPlusOneIncrementLoops.playground
示例:
在下面的示例中,您可以看到需要更改循环索引的位置。
有一个printObject
数组,其中包含PrintObject
个类,用于定义应将printText
打印到调试区域的次数。 printCount
属性是随机生成的,因此在循环之前不知道。
class PrintObject {
var printCount: Int = 0
var printText: String = "<Missing>"
}
var printObjects = [PrintObject]()
for i in 0...6 {
let randomCount = Int(arc4random_uniform(2)) + 1
let printObject = PrintObject()
printObject.printCount = randomCount
printObject.printText = "This is line \(i)"
printObjects.append(printObject)
}
printObjects.loopIncrement { (i) in
let printObject = printObjects[i]
printObject.printCount -= 1
if printObject.printCount > 0 {
i -= 1
}
print(printObject.printText)
}
这显然是一个微不足道的例子,但它可能会使某些情况更清楚,在这种情况下,更改循环索引是有意义的。
答案 0 :(得分:0)
您可以实现Generator / SequenceType以便能够使用for-in-loop:
struct IrregularGenerator<T> : GeneratorType, SequenceType {
let getIncrement: T -> Int
var index: Int
let array: [T]
init(array: [T], start: Int, getIncrement: T -> Int) {
guard start >= 0 && start < array.count else {
preconditionFailure("start index is out of bounds")
}
self.array = array
self.index = start
self.getIncrement = getIncrement
}
mutating func next() -> T? {
guard index >= 0 && index < array.count else {
return nil
}
defer {
index += getIncrement(array[index])
}
return array[index]
}
}
let planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
for planet in IrregularGenerator(array: planets, start: planets.count - 1, getIncrement: { ($0 == "Venus" || $0 == "Saturn") ? -3 : -1 }) {
print(planet)
}
这是用我的IrregularGenerator重写的PrintObject示例(我改为删除0增量保护,请注意,这可能会引发无限循环):
for printObject in IrregularGenerator(
array: printObjects, start: 0,
getIncrement: { $0.printCount -= 1; return $0.printCount == 0 ? 1 : 0 }
) {
print(printObject.printText)
}
答案 1 :(得分:0)
如果您只想要每个第二个元素
evens = Array(1...10).filter { $0 % 2 == 0 }
许多详细的代码
func isEven(number: Int) -> Bool {
return number % 2 == 0
}
evens = Array(1...10).filter(isEven)
答案 2 :(得分:0)
此外,如果您事先知道序列 ,则Swift的 Stridable 类型(即 Int )具有stride(to:by:)
和stride(through:by:)
方法。像这样使用它:
for i in 0.stride(to: 10, by: 2) {
// ...
}
i
将采用0
,2
,4
,6
,8
的值。
答案 3 :(得分:0)
我的方法是编写一个专门用于“行星”的全局函数:
func planetIncrement(planet: String) -> Int {
return planet == "Venus" || planet == "Saturn" ? 2 : 1
}
然后只需使用while
循环中的函数:
var i = 0
while i < planets.count {
i += planetIncrement(planets[i])
}
好处是函数和while
循环都很简单易读。折磨和复杂的循环逻辑是错误的一个配方,所以我会保持简单。
你写的loopIncrement
和loopDecrement
函数非常酷,但它们可能有点过分,并且要求你基本上为每个不同的用例编写一个完整的函数。
另一种可以重写while
循环的方法是使用递归函数:
func iteratePlanets(planets: [String], index i: Int) {
switch i {
case let i where i >= planets.count:
return
case let i where planets[i] == "Venus" || planets[i] == "Saturn"
iteratePlanets(planets, index: i + 2)
case let i:
iteratePlanets(planets, index: i + 1)
}
}
这与while
循环完全相同。
编辑:
也许这样的事情会起作用:
extension Array {
func loop(start: Int = 0, condition: (Int -> Bool)? = nil, increment: Element -> Int = { _ in 1 }, action: (Element -> ())? = nil) {
var index = start
while condition?(index) ?? (index < self.count) {
action?(self[index])
index += increment(self[index])
}
}
}
它循环,并允许您提供自定义条件来终止循环,更改增量,甚至在循环内执行操作,如下所示:
planets.loop(increment: { planet in
return planet == "Venus" || planet == "Saturn" ? 2 : 1
})
但是,我自己的感觉是这会掩盖代码。由于每个用例都有自己的内部循环逻辑,因此每次都必须重新编写逻辑。这意味着你每次都要写一个新函数。所以你不妨在循环中编写逻辑,或编写一个简单易懂的外部函数,以便一切都清晰。
也可以将此概括为所有CollectionType
s:
extension CollectionType {
func loop(start start: Index.Distance = 0, condition: (Index -> Bool)? = nil, increment: Generator.Element -> Index.Distance = { _ in 1 }, action: (Generator.Element -> ())? = nil) {
var index = self.startIndex.advancedBy(start)
while condition?(index) ?? (index.distanceTo(self.endIndex) > 0) {
let item = self[index]
action?(item)
index = index.advancedBy(increment(item))
}
}
}
例如,这会迭代Set
的所有元素,打印每个项目......
Set([1, 2, 3, 4, 5]).loop(action: { print($0) })
...这会以相反的顺序迭代一个数组,打印每个项目:
[1, 2, 3, 4, 5].loop(start: 4, condition: { $0 >= 0 }, increment: { _ in -1}, action: { print($0) })
它实际上很好地反映了C风格的循环。填写所有参数后,函数调用如下所示:
planets.loop(
start: 0,
condition: { $0 < planets.count },
increment: { $0 == "Venus" || $0 == "Saturn" ? 2 : 1 },
action: { print($0) })
使用您的printObjects
示例,您可以像这样使用它:
printObjects.loop(
increment: { $0.printCount >= 0 ? 0 : 1 },
action: {
$0.printText = "This is line \($0.printCount)"
print($0.printText)
$0.printCount -= 1 })
编辑:
我将函数参数名称从terminator
更改为condition
,以便在condition
返回true
时更清楚循环应该继续,与编写C风格的循环更为一致。
答案 4 :(得分:0)
我对此问题仍然有点不清楚,但这里有......
我认为实际问题看起来像这样:
let planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter"]
for (var i = 0; i < planets.count; i += 1) {
let string = planets[i]
if (string == "Venus" || string == "Saturn") {
i += 2
}
print(string)
}
这会产生:
Mercury
Venus
Jupiter
如果这是正确的,那么问题归结为:当我们在行星中循环时,我们最近看到金星(或土星)?如果我们最近看到它,请跳过此迭代。 Swift for&#34;跳过这个迭代&#34;是continue
。所以我们要做的就是跟踪我们最近看到金星的情况:
var marker = -100
for (ix,planet) in planets.enumerate() {
if ix - marker < 3 {
continue
}
if (planet == "Venus" || planet == "Saturn") {
marker = ix
}
print(planet)
}
话虽如此,我并不认为我会如何写这个。我要做的是缩短行星本身的清单。现在,您可以对剩余行星列表执行任何操作。所以现在我们的目标是从行星列表中删除跟随金星(或土星)的行星以及随后的行星:
let fewerPlanets = planets.enumerate().filter {
ix, planet in
for ix in [ix-1, ix-2] {
if ix >= 0 && (planets[ix] == "Venus" || planets[ix] == "Saturn") {
return false
}
}
return true
}.map {$1}
现在fewerPlanets
是["Mercury", "Venus", "Jupiter"]
,我们可以循环 并做我们想做的任何事情。