Расширьте типы массивов, используя предложение where в Swift

Я хотел бы использовать платформу Accelerate для расширения [Float] и [Double], но каждый из них требует другой реализации.

Я попробовал очевидное:

extension Array<Float> {
}

и получить эту ошибку:

«Ограниченное расширение должно быть объявлено для неспециализированного универсального типа «Массив» с ограничениями, указанными в предложении «где»

Можно ли таким образом расширить универсальные типы в Swift 2?

Теперь у меня работает код, как и ожидалось. Вот пример, показывающий суммирование с использованием платформы Accelerate.

extension _ArrayType where Generator.Element == Float {

    func quickSum() -> Float {
        var result: Float = 0
        if var x = self as? [Float] {
            vDSP_sve(&x, 1, &result, vDSP_Length(x.count))
        }
        return result
    }
}

extension _ArrayType where Generator.Element == Double {

    func quickSum() -> Double {
        var result: Double = 0
        if var x = self as? [Double] {
            vDSP_sveD(&x, 1, &result, vDSP_Length(x.count))
        }
        return result
    }
}

person GScrivs    schedule 04.08.2015    source источник


Ответы (8)


Если вы хотите расширить только массив определенного типа. Вы должны расширить протокол _ArrayType.

extension _ArrayType where Generator.Element == Int {

   func doSomething() {
       ... 
   }
}

Если вы расширяете Array, вы можете только убедиться, что ваш элемент соответствует другому протоколу. то есть:

extension Array where Element: Equatable {

   func doSomething() {
       ... 
   }
}

Обновлено: с Swift 3.1 https://github.com/apple/swift/blob/master/CHANGELOG.md

extension Array where Element == Int {

   func doSomething() {
       ... 
   }
}
person Huy Le    schedule 08.01.2016
comment
Это действительно помогло мне для немного другой проблемы. Я переопределял Array и использовал contains(element), который не работал, пока я не ограничил расширение до Element: Equatable. Чтобы узнать, какие существуют ограничения (и на какие методы они влияют), см. также SequenceType: swiftdoc.org /v2.1/протокол/SequenceType - person David James; 04.03.2016
comment
Я думаю, вам следует использовать SequenceType вместо _ArrayType - person Daniel; 13.07.2016
comment
Ответ отлично работает со Swift 2.2, но _ArrayType исчез в Swift 3. Как нам теперь поступить? - person Maiaux; 15.09.2016
comment
@Maiaux Смотрите мой ответ ниже. - person Ben Lu; 29.09.2016
comment
В swift 3.1 extension Array where Element == Int выдает ошибку ошибка: требование того же типа делает общий параметр «Элемент» неуниверсальным расширением Массив, где Элемент == Int, дайте мне предложение - person Rohit Parsana; 22.11.2016
comment
@BenLu Ваш ответ работает, но теперь у меня нет доступа к индексам для коллекции. Например, если я пишу self[0], я получаю сообщение Невозможно индексировать значение типа Self с индексом типа Int. - person Maiaux; 15.01.2017

Свифт 3 спешит на помощь!!

extension Collection where Iterator.Element == Int {
    // `Collection` can be `Sequence`, etc
}
person Ben Lu    schedule 28.09.2016
comment
Это работает, но теперь у меня нет доступа к индексам коллекции. Например, если я напишу self[0], я получу сообщение Не могу индексировать значение типа Self с индексом типа Int. - person Maiaux; 15.01.2017
comment
@Maiaux, потому что subscript не является функцией и не поддерживает дженерики, поэтому я думаю, что это не очень хорошо работает. - person Ben Lu; 20.01.2017
comment
Любое другое решение swift 3, поддерживающее индексы? - person Maiaux; 20.01.2017

Как насчет

extension CollectionType where Generator.Element == Double {

}

Или, если вы хотите немного больше:

protocol ArithmeticType {
    func +(lhs: Self, rhs: Self) -> Self
    func -(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
    func /(lhs: Self, rhs: Self) -> Self
}

extension Double : ArithmeticType {}
extension Float : ArithmeticType {}

extension SequenceType where Generator.Element : protocol<FloatLiteralConvertible, ArithmeticType> {
    var sum : Generator.Element {
        return reduce(0.0, combine: +)
    }

    var product : Generator.Element {
        return reduce(1.0, combine: *)
    }
}


stride(from: 1.0, through: 10.0, by: 1.0).sum   // 55
[1.5, 2.0, 3.5, 4.0, 5.5].product               // 231

Работает с Double и Float или любым другим типом, который соответствует протоколам ArithmeticType и FloatLiteralConvertible. Если вам нужно получить доступ к определенным индексам вашего массива, измените SequenceType на CollectionType, так как вы не можете сделать это с последовательностью.

person Kametrixom    schedule 04.08.2015

Если вы хотите расширить только определенный Array, вы должны использовать протокол для каждого типа:

protocol DoubleValue {
    var value: Double { get }
}
extension Double: DoubleValue {
    var value: Double { return self }
}
extension Array where Element: DoubleValue {
    // use the value property
}

// the same for Float
protocol FloatValue {
    var value: Float { get }
}

extension Float: FloatValue {
    var value: Float { return self }
}
extension Array where Element: FloatValue {
    // use the value property
}
person Qbyte    schedule 04.08.2015

Значит я не правильно прочитал вопрос. FloatingPointType — это существующий протокол, реализованный Double, Float и CGFloat, поэтому

Да. Я сделал это только вчера, чтобы добавить функцию в SequenceType, где элементы должны были быть Equatable. Это модификация для ограничения элементов до Float

Вам нужно использовать предложение where. Это моя функция ниже.

public extension SequenceType where Self.Generator.Element: FloatingPointType
{
    public func splitAt(separator: Generator.Element) -> [[Generator.Element]]
    {
        var ret: [[Generator.Element]] = []
        var thisPart: [Generator.Element] = []

        for element in self
        {
            if element == separator
            {
                ret.append(thisPart)
                thisPart = []
            }
            else
            {
                thisPart.append(element)
            }
        }
        ret.append(thisPart)
        return ret
    }
}

[Float(1), Float(2), Float(3), Float(4)].splitAt(Float(2))
// returns [[1],[3, 4]]
[Double(1), Double(2), Double(3), Double(4)].splitAt(Double(3))
// returns [[1, 2],[4]]

NB Я не мог заставить это работать для массива, но SequenceType в любом случае более общий.

person JeremyP    schedule 04.08.2015

Свифт 3 на Xcode 8.2

Просто нужно расширить протокол Sequence и указать оператор where.

let someString = "1, 2, 3, 4, 5, 6, 7, 8"

extension String {        
  func toArrayOfElements() -> [String] {
    return self.components(separatedBy: ", ")
  }        
}

extension Sequence where Iterator.Element == String {        
  func toInt() -> [Int] {            
    return self.map {
      Int($0)!
    }
  }        
}

let arrayOfStrings = someString.toArrayOfElements()    
print(arrayOfStrings)

let arrayOfInts = arrayOfStrings.toInt()    
print(arrayOfInts)
person Melvin John    schedule 23.02.2017

Это сработало для меня. Я использую Swift 5.

extension Array where Iterator.Element == Float {
}
person Sayalee Pote    schedule 17.06.2020

Вот что у меня сработало, используя Swift 5:

extension Array where ArrayLiteralElement == Float {

}
person pableiros    schedule 22.07.2021