【问题标题】:Explain Swift Iterators解释 Swift 迭代器
【发布时间】:2017-04-01 20:08:12
【问题描述】:

关于如何在 Swift 中创建生成器(或 iterators,因为它们在 Swift 中显然被称为)几乎没有最新的指导,特别是如果您是该语言的新手。为什么会有这么多像AnyIteratorUnfoldSequence 这样的生成器类型?为什么以下代码(应该从单个 Ints 序列或 Ints 数组中产生)不起作用?

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

按照我的理解,AnyIterator 应该是把{}s 中的闭包转换成返回的生成器中的.next() 方法,但它似乎不起作用。或者我应该像this question 那样使用UnfoldSequence。我很困惑。

【问题讨论】:

    标签: swift generator


    【解决方案1】:

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

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

    但是——不幸的是? – Swift 没有yield 声明,这意味着 闭包必须显式管理某些状态才能恢复迭代 每次调用的下一个元素。

    一种可能性是维护两个索引,一个用于 当前段和一个用于当前元素 如果是数组,则为段:

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

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

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

    还有待处理的数组与 他们目前的指数。数组本身没有被修改, 所以副本很便宜。

    例子:

    let G = chain([1, 2, [3]], 4, [5, 6, [], 7])
    while let g = G.next() {
        print(g)
    }
    // 1 2 3 4 5 6 7
    

    另请参阅Implementing recursive generator for simple tree structure in Swift 了解更多信息 递归枚举树状结构的方法。

    【讨论】:

    • 所以这会创建数组的副本?这不是生成器用来避免的吗?
    • @taylorswift:你的阵列有多大?有关系吗? - 但正如我所说,它肯定可以改进,它是为了解释总体思路。 – 例如,您可以(在第一种方法中)存储当前段的索引以及当前段中当前元素的索引(如果这是一个数组)。
    • 我们经常想使用生成器“一次”从许多块中构建一个连接或扁平化的数组,以绕过高级语言中缓慢的扩展方法。如果数组被复制,我们最好只使用重复的数组连接?或者这正是你应该在 Swift 中做的事情,我想得太像 python 程序员了
    • 这种生成器的想法是,生成器完成了定位应该进入数组的项目的所有工作,然后可以完成实际构建数组的复制操作一通
    • @taylorswift:如果您想创建一个包含所有元素的扁平“真实数组”,那么我假设不会有任何区别。如果您只想迭代所有元素,而不需要将它们放在数组中,那么生成器或(惰性)序列会更快,至少在您有很多对象的情况下。 – 但是,是的,那将是一个不同的问题。
    【解决方案2】:

    您还可以查看 https://stackoverflow.com/a/67215766/5867877 中所见的迭代器的这种自定义实现

    class ArrayIterator<T>{
    
    private var array : [T] = []
    private var stepSize: Int = 10
    private var head: Int = 0
    
    var hasNext: Bool {
        get {
            return head < array.count
        }
    }
    class func from(array: [T], stepSize size: Int = 10, startingAt startIndex: Int = 0) -> ArrayIterator<T>{
        
        let a = ArrayIterator<T>()
        a.array = array
        a.stepSize = size
        a.head = startIndex
        return a
    }
    
    func next() -> Array<T>? {
        guard head < array.count else {
            return nil
        }
        
        defer {
            head = head + stepSize
        }
        
        guard stepSize < array.count else {
            return array
        }
        
        if let _ = array[safe: (head + stepSize - 1)] {
            return Array(array[head..<head + stepSize])
            
        } else {
            let remaider = (head + stepSize - 1) % array.count
            return Array(array[head..<(head + stepSize - 1 - remaider)])
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 1970-01-01
      • 2021-01-29
      相关资源
      最近更新 更多