Swift 中的可变 Zip 函数 [英] Variadic Zip Function in Swift

查看:24
本文介绍了Swift 中的可变 Zip 函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近一直在使用 Apple 的原生 zip(_:_:),但遇到了需要zip 两个以上序列的情况.

I have been using Apple's native zip(_:_:) recently and I ran into a situation whereby I needed tozip more than two sequences.

于是我查找并找到了 的声明zip(_:_:) 在 Swift 的 GitHub 页面上.我获取了这些信息并能够重载 zip(_:_:) 以接受四个参数 zip(_:_:_:_:).我知道我可以煞费苦心地重载 zip 以支持我一次选择的任何数量的参数,但这是不灵活的、耗时的,而且我觉得可变参数选项会是一个更好的选择.

So I looked for and found the declaration of zip(_:_:) on Swift's GitHub page. I took that information and was able to overload zip(_:_:) to accept four parameters, zip(_:_:_:_:). I am aware that I can painstakingly overload zip to support whatever number of arguments I chose one at a time, but this is inflexible, time consuming, and I feel that a variadic option would be a much better choice.

如何让原生 zip 函数接受可以容纳任意数量的序列输入的可变参数.

How to make the native zip function accept a variadic parameter that can hold any number of sequence inputs.

/// Creates a sequence of pairs built out of two underlying sequences.
///
/// In the `Zip2Sequence` instance returned by this function, the elements of
/// the *i*th pair are the *i*th elements of each underlying sequence. The
/// following example uses the `zip(_:_:)` function to iterate over an array
/// of strings and a countable range at the same time:
///
///     let words = ["one", "two", "three", "four"]
///     let numbers = 1...4
///
///     for (word, number) in zip(words, numbers) {
///         print("(word): (number)")
///     }
///     // Prints "one: 1"
///     // Prints "two: 2
///     // Prints "three: 3"
///     // Prints "four: 4"
///
/// If the two sequences passed to `zip(_:_:)` are different lengths, the
/// resulting sequence is the same length as the shorter sequence. In this
/// example, the resulting array is the same length as `words`:
///
///     let naturalNumbers = 1...Int.max
///     let zipped = Array(zip(words, naturalNumbers))
///     // zipped == [("one", 1), ("two", 2), ("three", 3), ("four", 4)]
///
/// - Parameters:
///   - sequence1: The first sequence or collection to zip.
///   - sequence2: The second sequence or collection to zip.
/// - Returns: A sequence of tuple pairs, where the elements of each pair are
///   corresponding elements of `sequence1` and `sequence2`.
@inlinable // FIXME(sil-serialize-all)
public func zip<Sequence1, Sequence2>(
    _ sequence1: Sequence1, _ sequence2: Sequence2
    ) -> Zip2Sequence<Sequence1, Sequence2> {
    return Zip2Sequence(_sequence1: sequence1, _sequence2: sequence2)
}

/// A sequence of pairs built out of two underlying sequences.
///
/// In a `Zip2Sequence` instance, the elements of the *i*th pair are the *i*th
/// elements of each underlying sequence. To create a `Zip2Sequence` instance,
/// use the `zip(_:_:)` function.
///
/// The following example uses the `zip(_:_:)` function to iterate over an
/// array of strings and a countable range at the same time:
///
///     let words = ["one", "two", "three", "four"]
///     let numbers = 1...4
///
///     for (word, number) in zip(words, numbers) {
///         print("(word): (number)")
///     }
///     // Prints "one: 1"
///     // Prints "two: 2
///     // Prints "three: 3"
///     // Prints "four: 4"
@_fixed_layout // FIXME(sil-serialize-all)
public struct Zip2Sequence<Sequence1 : Sequence, Sequence2 : Sequence> {
    @usableFromInline // FIXME(sil-serialize-all)
    internal let _sequence1: Sequence1
    @usableFromInline // FIXME(sil-serialize-all)
    internal let _sequence2: Sequence2

    @available(*, deprecated, renamed: "Sequence1.Iterator")
    public typealias Stream1 = Sequence1.Iterator
    @available(*, deprecated, renamed: "Sequence2.Iterator")
    public typealias Stream2 = Sequence2.Iterator

    /// Creates an instance that makes pairs of elements from `sequence1` and
    /// `sequence2`.
    @inlinable // FIXME(sil-serialize-all)
    public // @testable
    init(_sequence1 sequence1: Sequence1, _sequence2 sequence2: Sequence2) {
        (_sequence1, _sequence2) = (sequence1, sequence2)
    }
}

extension Zip2Sequence {
    /// An iterator for `Zip2Sequence`.
    @_fixed_layout // FIXME(sil-serialize-all)
    public struct Iterator {
        @usableFromInline // FIXME(sil-serialize-all)
        internal var _baseStream1: Sequence1.Iterator
        @usableFromInline // FIXME(sil-serialize-all)
        internal var _baseStream2: Sequence2.Iterator
        @usableFromInline // FIXME(sil-serialize-all)
        internal var _reachedEnd: Bool = false

        /// Creates an instance around a pair of underlying iterators.
        @inlinable // FIXME(sil-serialize-all)
        internal init(
            _ iterator1: Sequence1.Iterator,
            _ iterator2: Sequence2.Iterator
            ) {
            (_baseStream1, _baseStream2) = (iterator1, iterator2)
        }
    }
}

extension Zip2Sequence.Iterator: IteratorProtocol {
    /// The type of element returned by `next()`.
    public typealias Element = (Sequence1.Element, Sequence2.Element)

    /// Advances to the next element and returns it, or `nil` if no next element
    /// exists.
    ///
    /// Once `nil` has been returned, all subsequent calls return `nil`.
    @inlinable // FIXME(sil-serialize-all)
    public mutating func next() -> Element? {
        // The next() function needs to track if it has reached the end.  If we
        // didn't, and the first sequence is longer than the second, then when we
        // have already exhausted the second sequence, on every subsequent call to
        // next() we would consume and discard one additional element from the
        // first sequence, even though next() had already returned nil.
        if _reachedEnd {
            return nil
        }

        guard let element1 = _baseStream1.next(),
            let element2 = _baseStream2.next() else {
                _reachedEnd = true
                return nil
        }

        return (element1, element2)
    }
}

extension Zip2Sequence: Sequence {
    public typealias Element = (Sequence1.Element, Sequence2.Element)

    /// Returns an iterator over the elements of this sequence.
    @inlinable // FIXME(sil-serialize-all)
    public func makeIterator() -> Iterator {
        return Iterator(
            _sequence1.makeIterator(),
            _sequence2.makeIterator())
    }
}

// @available(*, deprecated, renamed: "Zip2Sequence.Iterator")
public typealias Zip2Iterator<T, U> = Zip2Sequence<T, U>.Iterator where T: Sequence, U: Sequence

<小时>

zip(_:_:_:_:) 函数重载(我的实现)


zip(_:_:_:_:) Function Overload (my implementation)

/// Creates a sequence of pairs built out of four underlying sequences.
///
/// In the `Zip4Sequence` instance returned by this function, the elements of
/// the *i*th pair are the *i*th elements of each underlying sequence. The
/// following example uses the `zip(_:_:_:_:)` function to iterate over an array
/// of strings, an array of bolean values, an array of integer values, and a countable range, at the same time:
///
///     let evens = [false, true, false, true)
///     let squares = [1, 4, 9, 16]
///     let words = ["one", "two", "three", "four"]
///     let numbers = 1...4
///
///     for (word, number, even, square) in zip(words, numbers, evens, squares) {
///         print("(word): (number) --> (number) is (even ? "even" : "odd") and (number)² = (square)")
///     }
///     // Prints "one: 1 --> 1 is odd and 1² = 1"
///     // Prints "two: 2 --> 2 is even and 2² = 4"
///     // Prints "three: 3 --> 3 is odd and 3² = 9"
///     // Prints "four: 4 --> 4 is even and 4² = 16"
///
/// If the four sequences passed to `zip(_:_:_:_:)` are different lengths, the
/// resulting sequence is the same length as the shorter sequence. In this
/// example, the resulting array is the same length as `words`, `evens`, `and `squares`:
///
///     let naturalNumbers = 1...Int.max
///     let zipped = Array(zip(words, naturalNumbers, evens, squares))
///     // zipped == [("one", 1, false, 1), ("two", 2, true, 4), ("three", 3, false, 9), ("four", 4, true, 16)]
///
/// - Parameters:
///   - sequence1: The first sequence or collection to zip.
///   - sequence2: The second sequence or collection to zip.
///   - sequence3: The third sequence or collection to zip.
///   - sequence4: The fourth sequence or collection to zip.
/// - Returns: A sequence of tuple pairs, where the elements of each pair are
///   corresponding elements of `sequence1`, `sequence2`, `sequence3`, and `sequence3`.
public func zip<Sequence1, Sequence2, Sequence3, Sequence4>(
    _ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3, _ sequence4: Sequence4
    ) -> Zip4Sequence<Sequence1, Sequence2, Sequence3, Sequence4> {
    return Zip4Sequence(_sequence1: sequence1, _sequence2: sequence2, _sequence3: sequence3, _sequence4: sequence4)
}

/// A sequence of pairs built out of four underlying sequences.
///
/// In a `Zip4Sequence` instance, the elements of the *i*th pair are the *i*th
/// elements of each underlying sequence. To create a `Zip4Sequence` instance,
/// use the `zip(_:_:_:_:)` function.
///
/// The following example uses the `zip(_:_:_:_:)` function to iterate over an array
/// of strings, an array of bolean values, an array of integer values, and a countable range, at the same time:
////
///     let evens = [false, true, false, true)
///     let squares = [1, 4, 9, 16]
///     let words = ["one", "two", "three", "four"]
///     let numbers = 1...4
///
///     for (word, number, even, square) in zip(words, numbers, evens, squares) {
///         print("(word): (number) --> (number) is (even ? "even" : "odd") and (number)² = (square)")
///     }
///     // Prints "one: 1 --> 1 is odd and 1² = 1"
///     // Prints "two: 2 --> 2 is even and 2² = 4"
///     // Prints "three: 3 --> 3 is odd and 3² = 9"
///     // Prints "four: 4 --> 4 is even and 4² = 16"
@_fixed_layout // FIXME(sil-serialize-all)
public struct Zip4Sequence<Sequence1 : Sequence, Sequence2 : Sequence, Sequence3 : Sequence, Sequence4 : Sequence> {
    internal let _sequence1: Sequence1
    internal let _sequence2: Sequence2
    internal let _sequence3: Sequence3
    internal let _sequence4: Sequence4

    @available(*, deprecated, renamed: "Sequence1.Iterator")
    public typealias Stream1 = Sequence1.Iterator
    @available(*, deprecated, renamed: "Sequence2.Iterator")
    public typealias Stream2 = Sequence2.Iterator
    @available(*, deprecated, renamed: "Sequence3.Iterator")
    public typealias Stream3 = Sequence3.Iterator
    @available(*, deprecated, renamed: "Sequence4.Iterator")
    public typealias Stream4 = Sequence4.Iterator

    /// Creates an instance that makes pairs of elements from `sequence1` and
    /// `sequence2`.
    public // @testable
    init(_sequence1 sequence1: Sequence1, _sequence2 sequence2: Sequence2, _sequence3 sequence3: Sequence3, _sequence4 sequence4: Sequence4) {
        (_sequence1, _sequence2, _sequence3, _sequence4) = (sequence1, sequence2, sequence3, sequence4)
    }
}

extension Zip4Sequence {
    /// An iterator for `Zip4Sequence`.
    @_fixed_layout // FIXME(sil-serialize-all)
    public struct Iterator {
        internal var _baseStream1: Sequence1.Iterator
        internal var _baseStream2: Sequence2.Iterator
        internal var _baseStream3: Sequence3.Iterator
        internal var _baseStream4: Sequence4.Iterator
        internal var _reachedEnd: Bool = false

        /// Creates an instance around a set of 4 underlying iterators.
        internal init(
            _ iterator1: Sequence1.Iterator,
            _ iterator2: Sequence2.Iterator,
            _ iterator3: Sequence3.Iterator,
            _ iterator4: Sequence4.Iterator
            ) {
            (_baseStream1, _baseStream2, _baseStream3, _baseStream4) = (iterator1, iterator2, iterator3, iterator4)
        }
    }
}

extension Zip4Sequence.Iterator: IteratorProtocol {
    /// The type of element returned by `next()`.
    public typealias Element = (Sequence1.Element, Sequence2.Element, Sequence3.Element, Sequence4.Element)

    /// Advances to the next element and returns it, or `nil` if no next element
    /// exists.
    ///
    /// Once `nil` has been returned, all subsequent calls return `nil`.
    public mutating func next() -> Element? {
        // The next() function needs to track if it has reached the end. If we
        // didn't, and the first sequence is longer than the second, third, and
        // fourth, then when we have already exhausted the second, third, and
        // fourth sequence, on every subsequent call to next() we would consume
        // and discard one additional element from the first sequence, even
        // though next() had already returned nil.
        if _reachedEnd {
            return nil
        }

        guard let element1 = _baseStream1.next(),
            let element2 = _baseStream2.next(),
            let element3 = _baseStream3.next(),
            let element4 = _baseStream4.next()
            else {
                _reachedEnd = true
                return nil
        }

        return (element1, element2, element3, element4)
    }
}

extension Zip4Sequence: Sequence {
    public typealias Element = (Sequence1.Element, Sequence2.Element, Sequence3.Element, Sequence4.Element)

    /// Returns an iterator over the elements of this sequence.
    public func makeIterator() -> Iterator {
        return Iterator(
            _sequence1.makeIterator(),
            _sequence2.makeIterator(),
            _sequence3.makeIterator(),
            _sequence4.makeIterator())
    }
}

@available(*, deprecated, renamed: "Zip4Sequence.Iterator")
public typealias Zip4Iterator<T, U, V, W> = Zip4Sequence<T, U, V, W>.Iterator where T: Sequence, U: Sequence, V: Sequence, W: Sequence

警告: 此重载仅适用于固定数量的输入,而理想情况下我正在寻找一个 zip 函数,它可以接受不分青红皂白数量的 AnySequence 输入.

Caveat: This overload only is for a fixed number of inputs whereas I am ideally looking for an zip function that accepts an indiscriminate number of AnySequence inputs.

重申一下,我希望创建一个 zip(_: AnySequence...) 函数,我可以使用它来压缩任意数量的序列 通过使用 variadic 参数.

Just to reiterate, I am looking to create a zip(_: AnySequence...) function that I can use to zip any number of sequences through the use of a variadic parameter.

提前感谢大家的帮助,我真的很感激!

Thank you everyone in advance for all of your help, I really appreciate it!

推荐答案

遗憾的是,这在 Swift 中并不完全可行,因为我们不能为可变参数设置不同的数组类型.一种可能的解决方法是将所有数组转换为 [Any] 类型,然后将它们全部压缩在一起形成一个数组数组,然后使用 map 转换数组使用强制转换到想要的元组.解决方案可能有点脏,但不是太复杂.

Sadly, this is not completely possible in Swift since we can't have different Array types for Variadic parameters. A possible work around would be to convert all arrays to type [Any], and then zip them all together which forms an array array, and than using map, converting the arrays to the wanted tuple using force casting. The solution might be bit dirty but not all too complicated.

extension Sequence {

    //easy conversion of any sequence to [Any]
    var untyped:[Any] {
        return self as! [Any]
   }
}

func zip(_ untypedSequences:[Any]...) -> [[Any]] {
    let count = untypedSequences.map{$0.count}.min() ?? 0
    return (0..<count).map{ index in untypedSequences.map{ $0[index] } }
}

//Test
let test1 = ["a", "b", "c"]
let test2 = [1, 2, 3, 4]
let test3 = [7.2, 12.23, 1.3]

let zippedTests = zip(test1.untyped, test2.untyped, test3.untyped).map {
    ($0[0] as! String, $0[1] as! Int, $0[2] as! Double)
}

这篇关于Swift 中的可变 Zip 函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆