2016-09-16 7 views
1

В Swift 2 я смог написать функцию, которая работала в любой последовательности, например, (String, Int). Это выглядело примерно так:Как ограничить общий параметр последовательности для кортежа в Swift 3?

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

Использование кортежа в качестве ограниченного типа был особенно полезен тем чтобы он мог принять словари, такие как [String:Int], так как их тип последовательности состоит из кортежей.

В Swift 3, я считаю, подобная функция будет:

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

Но попытка пройти в [String:Int], например: test(input: ["a": 1, "b": 2]), вызывает ошибку:

Generic parameter 'T' could not be inferred

Насколько Я могу сказать, что словари в Swift 3 по-прежнему используют кортеж (Key, Value) как свой тип итератора, поэтому я думаю, что это должно сработать. На самом деле, если я не использую один тип в качестве типа итератора с ограничениями, например where T.Iterator.Element == String, я могу передать что-то вроде [String] и он работает правильно.

Есть ли что-то, что мне не хватает, или это может быть регрессия в Swift 3?

ответ

1

Интересный пример.

Давайте проверим определение Dictionary о соответствии с Sequence:

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

А потом DictionaryIterator:

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

Так, Dictionary, T.Iterator.Element кажется, (key: Key, value: Value), не (Key, Value).

Если переписать функцию:

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

это работает:

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

Но это не работает:

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

Я не уверен, что это намеренное особенность или какой-то регресс, или просто ошибка.

+0

На самом деле, я думаю, я помню, что читал, что кортежи теперь более строгие в отношении своих ярлыков, поэтому это может быть особенностью. Я посмотрю, смогу ли я его найти ... – Robert

+0

Кажется, я не могу найти его сейчас, но это в основном решает проблему для меня в любом случае. Благодаря! – Robert

1

Это известная ошибка (SR-992), которая в некоторых случаях не позволяет компилятору сопоставлять кортежи того же типа, но разные метки.

Одним из возможных обходной путь, который позволил бы вам обоим прохода последовательностей (String, Int) или (key: String, value: Int) кортежей и [String : Int] словаря будет перегружать test(input:) с функцией, которая ожидает последовательности с (key: String, value: Int) элемента в качестве входных данных. Затем вы можете использовать лениво оцененный 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 

Возможно, не самое идеальное решение - но, насколько я могу видеть, что нет никакой другой простой обходной путь для этой проблемы. Было бы интересно, если у кого-нибудь есть лучшие идеи.

+0

Спасибо, так что это ошибка. Не слишком неудобно обходиться, но, надеюсь, скоро это будет исправлено! – Robert