[英]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
: 让我们检查关于符合Sequence
的Dictionary
的定义:
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)
. 因此,对于Dictionary
, T.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。但是,它可能会有所帮助。
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.