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

查看:59
本文介绍了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中这是不可能完全实现的,因为我们不能为Variadic参数使用不同的数组类型.可能的解决方法是将所有数组转换为类型[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天全站免登陆