解释Swift迭代器

时间:2016-11-18 04:05:27

标签: swift generator

关于如何在Swift中创建生成器(或者 iterators ,因为它们显然在Swift中调用)的指南很少,特别是如果您不熟悉该语言。为什么有AnyIteratorUnfoldSequence这么多的生成器类型?为什么以下代码不应该从单个Int的序列或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)
}

我理解它的方式,AnyIterator应该在{} s中进行闭包并将其转换为返回的生成器中的.next()方法,但它似乎不是工作。或者我应该在this question中使用UnfoldSequence代替。我很困惑。

2 个答案:

答案 0 :(得分:4)

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

如果Swift有一个yield语句,比如Python或C#那么事情会更容易:你可以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 递归枚举树状结构的方法。

答案 1 :(得分:0)

您还可以查看此迭代器的自定义实现,如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)])
    }
}