2016-06-24 9 views
-1

Использование Xcode 8 beta, swift 3, второе расширение не может быть скомпилировано. Я не понимаю, является ли это быстрой ошибкой или известным ограничением.Почему система типа Swift пытается преобразовать тип в неверный ожидаемый параметр

extension Array { 
    func scanl<T>(initial: T, combine:(Iterator.Element, T) -> T) -> [T] { 
     guard let first = self.first else { return [] } 
     return [initial] + Array(self.dropFirst()).scanl(initial: combine(first, initial), combine: combine) 
    } 
} 


extension Array { 
    func scanl<T>(combine: (Iterator.Element, T) -> T) -> [T] { 
     guard let first = self.first else { return [] } 
     return Array(self.dropFirst()).scanl(initial:first, combine:combine)// Cannot convert value of type '(Element, T) -> T' To expected argument type '(_, _) -> _' 
    } 
} 

(Element, T) -> T действительно является типом функции. Так я не могу понять, почему компилятор ожидать (,) -> __ и что этот тип означает, кроме «я не забочусь о типе»

+0

Я бы отказался от использования рекурсии здесь, потому что он будет копировать весь массив каждый вызов, что приведет к сложности времени «O (n^2)». Вы лучше, просто используя 'for'-loop. –

ответ

1

Это не ошибка или ограничение, это просто невозможно для компилятора установить во время компиляции, что first имеет тип T в вашем втором расширении (как T необязательно может быть таким же, как Iterator.Element). В обоих ваших замыканиях компилятор знает, что first имеет тип Iterator.Element, но компилятор не может знать, является ли это также тип T.

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

В вашем втором продлении, однако, вы пытаетесь передать first в качестве аргумента параметра (initial), который ожидает типа T, и компилятор не может знать, действительно ли first имеет тип T (того же типа T, которые используются замыкание combine, используемое для вызова двух аргументов scanl), а именно, что Iterator.Element из self имеет тип T. Это можно легко исправить путем попытки преобразования типа (as?) first в T в необязательное предложение привязки этого второго расширения.

extension Array { 
    func scanl<T>(combine: (Iterator.Element, T) -> T) -> [T] { 
     guard let first = self.first as? T else { return [] } 
     return Array(self.dropFirst()).scanl(initial: first, combine: combine) 
    } 
} 

Тот факт, что Iterator.Element и T не обязательно должен быть того же типа, очевидно, если вы построить пример, который сканирует массив одного типа, чтобы построить массив другого типа, например,

/* scant [Int] array to construct [String] array */ 
let foo = [1, 2, 3, 4, 5] 
let bar = foo.scanl(initial: "0") { String($0) + $1 } 
print(bar) // ["0", "10", "210", "3210", ""] 

Если бы вы только как ваш метод scanl и его коллектора для получения массивов же типа (как один сканируется), то вам не нужно включать в общий T, но можно использовать Iterator.Element типа на месте от T в ваших расширениях выше.