解释Swift迭代器

tay*_*ift 7 generator swift

关于如何在Swift中创建生成器(或者在Swift中显然调用它们的迭代器)的指南很少,特别是如果您不熟悉该语言.为什么有这么多的发电机类型AnyIteratorUnfoldSequence?为什么下面的代码不应该从单个Ints或s的数组中产生Int

func chain(_ segments: Any...) -> AnyIterator<Int>{
    return AnyIterator<Int> {
        for segment in segments {
            switch segment {
            case let segment as Int:
                return segment
            case let segment as [Int]:
                for i in segment {
                    return i
                }
            default:
                return nil
            }
        }
        return nil
    }
}

let G = chain(array1, 42, array2)
while let g = G.next() {
    print(g)
}
Run Code Online (Sandbox Code Playgroud)

我理解它的方式,AnyIterator应该采取{}s中的闭包并将其转换.next()为返回的生成器中的方法,但它似乎不起作用.或者我应该UnfoldSequence这个问题一样使用.我很困惑.

Mar*_*n R 4

是的, 的next()方法AnyIterator调用给定的闭包。\n在您的代码中,该闭包在每次调用时返回相同的第一个元素,因为它不记得已经返回了哪些元素。

\n\n

如果 Swift 有yield像 Python 或 C# 这样的语句,那么事情会更容易:你可以yield segmentyield i已经完成了。

\n\n

但不幸的是\xe2\x80\x93?\xe2\x80\x93 Swift 没有yield语句,这意味着闭包必须显式管理某些状态,以便在每次调用时恢复与下一个元素的迭代。

\n\n

一种可能是维护两个索引,一个用于当前段,一个用于段内的当前元素(如果该索引是数组):

\n\n
func chain(_ segments: Any...) -> AnyIterator<Int> {\n    var currentSegment = 0 // index of current segment\n    var currentElement = 0 // index of current element within current segment\n    return AnyIterator<Int> {\n        while currentSegment < segments.count {\n            let next = segments[currentSegment]\n            switch next {\n            case let value as Int:\n                currentSegment += 1\n                return value\n            case let segment as [Int]:\n                if currentElement < segment.count {\n                    let val = segment[currentElement]\n                    currentElement += 1\n                    return val\n                }\n                currentSegment += 1\n                currentElement = 0\n            default:\n                return nil\n            }\n        }\n        return nil\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这可以推广到任意嵌套的数组:

\n\n
func chain(_ segments: Any...) -> AnyIterator<Int> {\n    var stack: [(Any, Int)] = [(segments, 0)]\n    return AnyIterator<Int> {\n        while let (next, idx) = stack.popLast() {\n            switch next {\n            case let value as Int:\n                return value\n            case let segments as [Any]:\n                if idx < segments.count {\n                    stack.append((segments, idx + 1))\n                    stack.append((segments[idx], 0))\n                }\n            default:\n                return nil\n            }\n        }\n        return nil\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

仍待处理的数组与其当前索引一起位于堆栈上。数组本身不会被修改,\n因此副本很便宜。

\n\n

例子:

\n\n
let G = chain([1, 2, [3]], 4, [5, 6, [], 7])\nwhile let g = G.next() {\n    print(g)\n}\n// 1 2 3 4 5 6 7\n
Run Code Online (Sandbox Code Playgroud)\n\n

另请参阅在 Swift 中实现简单树结构的递归生成器,了解更多\n递归枚举树状结构的方法。

\n