Как я могу расширить типизированные массивы в Swift?



как я могу продлить Свифт Array<T> или T[] тип с пользовательскими функциональными utils?



просмотр документов API Swift показывает, что методы массива являются расширением T[], электронной.г:



extension T[] : ArrayType {
//...
init()

var count: Int { get }

var capacity: Int { get }

var isEmpty: Bool { get }

func copy() -> T[]
}


при копировании и вставке одного и того же источника и попытке любых изменений, таких как:



extension T[] : ArrayType {
func foo(){}
}

extension T[] {
func foo(){}
}


он не может построить с ошибкой:




номинального типа T[] не может быть продлен




использование полного типа определение терпит неудачу с Use of undefined type 'T', я.е:



extension Array<T> {
func foo(){}
}


и он также терпит неудачу с Array<T : Any> и Array<String>.



любопытно Swift позволяет мне расширить нетипизированный массив с:



extension Array {
func each(fn: (Any) -> ()) {
for i in self {
fn(i)
}
}
}


который он позволяет мне позвонить с:



[1,2,3].each(println)


но я не могу создать правильное расширение универсального типа, поскольку тип, похоже, теряется, когда он проходит через метод, например, пытается заменить встроенный фильтр Swift:



extension Array {
func find<T>(fn: (T) -> Bool) -> T[] {
var to = T[]()
for x in self {
let t = x as T
if fn(t) {
to += t
}
}
return to
}
}


но компилятор рассматривает его как untyped, где он все еще позволяет вызывать расширение с помощью:



["A","B","C"].find {  > "A" }


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



["A","B","C"].find { ( as String).compare("A") > 0 }


кто-нибудь знает, как правильно создать типизированный метод расширения, который действует как встроенные расширения?

581   9  

9 ответов:

для расширения типизированных массивов с помощью классы, ниже работает для меня (Swift 2.2). Например, сортировка типизированного массива:

class HighScoreEntry {
    let score:Int
}

extension Array where Element:HighScoreEntry {
    func sort() -> [HighScoreEntry] {
      return sort { .score < .score }
    }
}

пытаясь сделать это с struct или typealias выдаст ошибку:

Type 'Element' constrained to a non-protocol type 'HighScoreEntry'

обновление:

для расширения типизированных массивов с помощью для занятия использовать следующий подход:

typealias HighScoreEntry = (Int)

extension SequenceType where Generator.Element == HighScoreEntry {
    func sort() -> [HighScoreEntry] {
      return sort {  <  }
    }
}

на Swift 3 некоторые типы были переименованы:

extension Sequence where Iterator.Element == HighScoreEntry 
{
    // ...
}

через некоторое время, пытаясь разные вещи решение, кажется, удалить <T> с подписью типа:

extension Array {
    func find(fn: (T) -> Bool) -> [T] {
        var to = [T]()
        for x in self {
            let t = x as T;
            if fn(t) {
                to += t
            }
        }
        return to
    }
}

который теперь работает по назначению без ошибок сборки:

["A","B","C"].find { .compare("A") > 0 }

У меня была аналогичная проблема - я хотел расширить общий массив с помощью метода swap (), который должен был принимать аргумент того же типа, что и массив. Но как вы определяете общий тип? Я нашел методом проб и ошибок, что ниже работал:

extension Array {
    mutating func swap(x:[Element]) {
        self.removeAll()
        self.appendContentsOf(x)
    }
}

ключом к нему было слово "элемент". Обратите внимание, что я нигде не определял этот тип, он, кажется, автоматически существует в контексте расширения массива и ссылается на любой тип элементов массива есть.

Я не уверен на 100%, что там происходит, но я думаю, что это, вероятно, потому, что "элемент" является связанным типом массива (см. "связанные типы" здесь https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189)

однако, я не вижу никакой ссылки на это в ссылке на структуру массива (https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//apple_ref/swift/struct/s:Sa) ... так что я все еще немного не уверен.

Использование Swift 2.2: Я столкнулся с подобной проблемой при попытке удалить дубликаты из массива строк. Я смог добавить расширение в класс Array, которое делает именно то, что я искал.

extension Array where Element: Hashable {
    /**
     * Remove duplicate elements from an array
     *
     * - returns: A new array without duplicates
     */
    func removeDuplicates() -> [Element] {
        var result: [Element] = []
        for value in self {
            if !result.contains(value) {
                result.append(value)
            }
        }
        return result
    }

    /**
     * Remove duplicate elements from an array
     */
    mutating func removeDuplicatesInPlace() {
        var result: [Element] = []
        for value in self {
            if !result.contains(value) {
                result.append(value)
            }
        }
        self = result
    }
}

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

 var dupes = ["one", "two", "two", "three"]
 let deDuped = dupes.removeDuplicates()
 dupes.removeDuplicatesInPlace()
 // result: ["one", "two", "three"]

Я посмотрел на заголовки стандартной библиотеки Swift 2, и вот прототип функции фильтра, что делает совершенно очевидным, как свернуть свой собственный.

extension CollectionType {
    func filter(@noescape includeElement: (Self.Generator.Element) -> Bool) -> [Self.Generator.Element]
}

это не расширение массива, а CollectionType, поэтому тот же метод применяется к другим типам коллекций. @noescape означает, что переданный блок не покинет область действия функции фильтра, что позволяет выполнить некоторые оптимизации. Самость с большой буквы - это класс, который мы расширяем. Личность.Генератор итератор, который выполняет итерацию по объектам в коллекции и Self.Генератор.Элемент-это тип объектов, например для массива [Int?] Личность.Генератор.Элемент будет Int?.

в целом этот метод фильтра может быть применен к любому типу CollectionType, ему нужен блок фильтра, который принимает элемент коллекции и возвращает Bool, и он возвращает массив исходного типа. Итак, собирая это вместе, вот метод, который я нахожу полезным: он сочетает в себе карту и фильтр, путем принимая блок, который сопоставляет элемент коллекции с необязательным значением и возвращает массив тех необязательных значений, которые не равны нулю.

extension CollectionType {

    func mapfilter<T>(@noescape transform: (Self.Generator.Element) -> T?) -> [T] {
        var result: [T] = []
        for x in self {
            if let t = transform (x) {
                result.append (t)
            }
        }
        return result
    }
}

Если вы хотите узнать о расширении массивов и других типах сборки в классах checkout code в этом репозитории githubhttps://github.com/ankurp/Cent

начиная с Xcode 6.1 синтаксис расширения массивов выглядит следующим образом

extension Array {
    func at(indexes: Int...) -> [Element] {
        ... // You code goes herer
    }
}
import Foundation

extension Array {
    var randomItem: Element? {
        let idx = Int(arc4random_uniform(UInt32(self.count)))
        return self.isEmpty ? nil : self[idx]
    }
}

(Swift 2.x)

вы также можете расширить массив, чтобы он соответствовал протоколу, содержащему синие rpints для методов универсального типа, например, протокол, содержащий ваши пользовательские функциональные utils для всех элементов универсального массива, соответствующих некоторому ограничению типа, скажем protocol MyTypes. Бонус использования этого подхода заключается в том, что вы можете писать функции, принимающие общие аргументы массива, с ограничением, что эти аргументы массива должны соответствовать вашим пользовательским функции протокола утилиты, скажем протоколом MyFunctionalUtils.

вы можете получить это поведение либо неявно, по типу ограничения элементов массива до MyTypes, или-- - как я покажу в методе, который я описываю ниже - - -, довольно аккуратно, явно, позволяя вашему заголовку общих функций массива напрямую показать, что входные массивы соответствуют MyFunctionalUtils.


начнем с протоколами MyTypes для использования в качестве ограничения типа; расширьте типы, которые вы хотите вписать в свои универсальные шаблоны следующим образом протокол (пример ниже расширяет основные типы Int и Double а также пользовательский тип MyCustomType)

/* Used as type constraint for Generator.Element */
protocol MyTypes {
    var intValue: Int { get }
    init(_ value: Int)
    func *(lhs: Self, rhs: Self) -> Self
    func +=(inout lhs: Self, rhs: Self)
}

extension Int : MyTypes { var intValue: Int { return self } }
extension Double : MyTypes { var intValue: Int { return Int(self) } }
    // ...

/* Custom type conforming to MyTypes type constraint */
struct MyCustomType : MyTypes {
    var myInt : Int? = 0
    var intValue: Int {
        return myInt ?? 0
    }

    init(_ value: Int) {
        myInt = value
    }
}

func *(lhs: MyCustomType, rhs: MyCustomType) -> MyCustomType {
    return MyCustomType(lhs.intValue * rhs.intValue)
}

func +=(inout lhs: MyCustomType, rhs: MyCustomType) {
    lhs.myInt = (lhs.myInt ?? 0) + (rhs.myInt ?? 0)
}

протокол MyFunctionalUtils (проведение чертежей наши дополнительные общие функции массива утилиты) и после этого, расширение массива на MyFunctionalUtils; реализация метода(ов) с синей печатью:

/* Protocol holding our function utilities, to be used as extension 
   o Array: blueprints for utility methods where Generator.Element 
   is constrained to MyTypes */
protocol MyFunctionalUtils {
    func foo<T: MyTypes>(a: [T]) -> Int?
        // ...
}

/* Extend array by protocol MyFunctionalUtils and implement blue-prints 
   therein for conformance */
extension Array : MyFunctionalUtils {
    func foo<T: MyTypes>(a: [T]) -> Int? {
        /* [T] is Self? proceed, otherwise return nil */
        if let b = self.first {
            if b is T && self.count == a.count {
                var myMultSum: T = T(0)

                for (i, sElem) in self.enumerate() {
                    myMultSum += (sElem as! T) * a[i]
                }
                return myMultSum.intValue
            }
        }
        return nil
    }
}

наконец, тесты и два примера, показывающие функцию, принимающую общие массивы, со следующими случаями, соответственно

  1. показывает подразумевается утверждение, что параметры массива соответствуют протоколу "MyFunctionalUtils", через тип, ограничивающий элементы массивов "MyTypes" (function bar1).

  2. показывает явно что параметры массива соответствуют протоколу 'MyFunctionalUtils' (function bar2).

тест и примеры следующим образом:

/* Tests & examples */
let arr1d : [Double] = [1.0, 2.0, 3.0]
let arr2d : [Double] = [-3.0, -2.0, 1.0]

let arr1my : [MyCustomType] = [MyCustomType(1), MyCustomType(2), MyCustomType(3)]
let arr2my : [MyCustomType] = [MyCustomType(-3), MyCustomType(-2), MyCustomType(1)]

    /* constrain array elements to MyTypes, hence _implicitly_ constraining
       array parameters to protocol MyFunctionalUtils. However, this
       conformance is not apparent just by looking at the function signature... */
func bar1<U: MyTypes> (arr1: [U], _ arr2: [U]) -> Int? {
    return arr1.foo(arr2)
}
let myInt1d = bar1(arr1d, arr2d) // -4, OK
let myInt1my = bar1(arr1my, arr2my) // -4, OK

    /* constrain the array itself to protocol MyFunctionalUtils; here, we
       see directly in the function signature that conformance to
       MyFunctionalUtils is given for valid array parameters */
func bar2<T: MyTypes, U: protocol<MyFunctionalUtils, _ArrayType> where U.Generator.Element == T> (arr1: U, _ arr2: U) -> Int? {

    // OK, type U behaves as array type with elements T (=MyTypes)
    var a = arr1
    var b = arr2
    a.append(T(2)) // add 2*7 to multsum
    b.append(T(7))

    return a.foo(Array(b))
        /* Ok! */
}
let myInt2d = bar2(arr1d, arr2d) // 10, OK
let myInt2my = bar2(arr1my, arr2my) // 10, OK
import Foundation

extension Array {

    func calculateMean() -> Double {
        // is this an array of Doubles?
        if self.first is Double {
            // cast from "generic" array to typed array of Doubles
            let doubleArray = self.map {  as! Double }

            // use Swift "reduce" function to add all values together
            let total = doubleArray.reduce(0.0, combine: { + })

            let meanAvg = total / Double(self.count)
            return meanAvg

        } else {
            return Double.NaN
        }
    }

    func calculateMedian() -> Double {
        // is this an array of Doubles?
        if self.first is Double {
            // cast from "generic" array to typed array of Doubles
            var doubleArray = self.map {  as! Double }

            // sort the array
            doubleArray.sort( { < } )

            var medianAvg : Double
            if doubleArray.count % 2 == 0 {
                // if even number of elements - then mean average the middle two elements
                var halfway = doubleArray.count / 2
                medianAvg = (doubleArray[halfway] + doubleArray[halfway - 1]) / 2

            } else {
                // odd number of elements - then just use the middle element
                medianAvg = doubleArray[doubleArray.count  / 2 ]
            }
            return medianAvg
        } else {
            return Double.NaN
        }

    }

}

Comments

    Ничего не найдено.