简体   繁体   English

如何在Swift 3中将通用序列参数约束到元组?

[英]How to constrain a generic sequence parameter to a tuple in Swift 3?

In Swift 2, I was able to write a function which operated on any sequence of, for example, (String, Int) . 在Swift 2中,我能够编写一个可对任何序列进行操作的函数,例如(String, Int) It looked something like this: 它看起来像这样:

func test<T: SequenceType where T.Generator.Element == (String, Int)>(input: T) {
    for (k, v) in input {
        print(k, "=", String(v))
    }
}

Using a tuple as the constrained type was particularly useful so that it could accept dictionaries, such as [String:Int] , as their sequence type consisted of tuples. 使用元组作为约束类型特别有用,因此它可以接受字典,例如[String:Int] ,因为它们的序列类型由元组组成。

In Swift 3, I believe a similar function would be: 在Swift 3中,我相信类似的功能将是:

func test<T: Sequence>(input: T) where T.Iterator.Element == (String, Int) {
    for (k, v) in input {
        print(k, "=", String(v))
    }
}

But attempting to pass in a [String:Int] , for example: test(input: ["a": 1, "b": 2]) , causes the error: 但是尝试传递[String:Int] ,例如: test(input: ["a": 1, "b": 2]) ,将导致错误:

Generic parameter 'T' could not be inferred 无法推断通用参数“ T”

As far as I can tell, dictionaries in Swift 3 still use a (Key, Value) tuple as their iterator type, so I think this should work. 据我所知,Swift 3中的字典仍然使用(Key,Value)元组作为其迭代器类型,因此我认为这应该可行。 In fact, if I don't use a single type as the constrained iterator type, such as where T.Iterator.Element == String , I can pass in something such as a [String] and it works correctly. 实际上,如果我不使用单一类型作为受约束的迭代器类型,例如where T.Iterator.Element == String ,那么我可以传入诸如[String]东西,并且它可以正常工作。

Is there something I'm missing, or might this be a regression in Swift 3? 我缺少什么,或者这可能是Swift 3中的回归?

An interesting example. 一个有趣的例子。

Let's check the definition of Dictionary about conforming to Sequence : 让我们检查关于符合SequenceDictionary的定义:

public func makeIterator() -> DictionaryIterator<Key, Value>

And then DictionaryIterator : 然后DictionaryIterator

public mutating func next() -> (key: Key, value: Value)?

So, for Dictionary , T.Iterator.Element seems to be (key: Key, value: Value) , not (Key, Value) . 因此,对于DictionaryT.Iterator.Element似乎是(key: Key, value: Value) ,而不是(Key, Value)

If you rewrite your function as: 如果将函数重写为:

func test<T: Sequence>(input: T) where T.Iterator.Element == (key: String, value: Int) {
    for (k, v) in input {
        print(k, "=", String(v))
    }
}

this works: 这有效:

test(input: ["a": 1, "b": 2])

But this does not work: 但这不起作用:

test(input: [("a", 1),("b",2)]) //->Generic parameter 'T' could not be inferred

I'm not sure this is an intended feature or some sort of regression, or simply a bug. 我不确定这是预期的功能还是某种回归,还是仅仅是一个错误。

This is known bug ( SR-992 ) which in some cases prevents the compiler from matching tuples of the same type, but different labels. 这是已知的错误( SR-992 ),在某些情况下,该错误会阻止编译器匹配相同类型但标签不同的元组。

One possible workaround that would allow you to both pass sequences of (String, Int) or (key: String, value: Int) tuples and a [String : Int] dictionary would be to overload test(input:) with a function that expects a sequence with a (key: String, value: Int) element as an input. 一种可能允许您同时传递(String, Int)(key: String, value: Int)元组和[String : Int]字典的序列的可能解决方法是使test(input:)重载期望的函数以(key: String, value: Int)元素作为输入的序列。 You could then use a lazily evaluated map in order to 'erase' the tuple labels and then pass that sequence onto your original implementation of test . 然后,您可以使用懒惰求值的map来“擦除”元组标签,然后将该序列传递给test的原始实现。

// Overload to deal with [String : Int] inputs – workaround for a bug.
func test<T: Sequence>(input: T) where T.Iterator.Element == (key: String, value: Int) {
    // 'Erase' the tuple labels with a lazily evaluated map(_:)
    // and pass the sequence onto the original implementation of test(input:)
    test(input: AnySequence(input.lazy.map{($0.key, $0.value)}))
}

func test<T: Sequence>(input: T) where T.Iterator.Element == (String, Int) {
    for (k, v) in input {
        print(k, "=", v)
    }
}

let d = ["foo" : 5]
let a = [("key", 3)]

test(input: d) // prints: foo = 5
test(input: a) // prints: key = 3

Perhaps not the most ideal solution – but as far as I can see there's no other easy workaround to this problem. 也许不是最理想的解决方案-但据我所知,没有其他简单的解决方法可以解决此问题。 Would certainly be interested if anyone has any better ideas. 如果有人有更好的想法,肯定会感兴趣。

It isn't for swift 3. However, it might help. 这不是快速的3。但是,它可能会有所帮助。

Swift 4 斯威夫特4

Parameterised extensions are not allowed yet. 尚不允许参数化扩展。 So, we need to use function. 因此,我们需要使用功能。

extension Result {
    func zip<A, B, C>(with other: Result<C, Failure>) -> Result<(A, B, C), Failure> where Success == (A, B) {
        return flatMap { (a, b) in
            Result<(A, B, C), Failure>.init(catching: {
                let c = try other.get()
                return (a, b, c)
            })
        }
    }
}

Where Result is Result在哪里

public enum Result<Success, Failure: Error> {
    /// A success, storing a `Success` value.
    case success(Success)

    /// A failure, storing a `Failure` value.
    case failure(Failure)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM