我如何扩展Swift的数组<T>或T[]类型与自定义功能utils?

浏览Swift的API文档可以发现Array方法是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[]不能扩展

使用完整的类型定义在使用未定义类型'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)

但是我不能创建一个适当的泛型类型扩展,因为类型似乎在它流经方法时丢失了,例如试图用:

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
    }
}

但是编译器将它视为无类型的,它仍然允许调用扩展:

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

当使用调试器进行步进时,则表示类型为Swift。字符串,但这是一个构建错误,试图访问它像一个字符串,而不首先将其转换为字符串,即:

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

有人知道创建类似内置扩展的类型化扩展方法的正确方法吗?


当前回答

(迅速2. x)

您还可以扩展数组以符合包含泛型类型方法的blue-rpints的协议,例如,包含符合某种类型约束的所有泛型数组元素的自定义函数utils的协议,例如协议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
    }
}

最后,测试和两个示例显示了接受泛型数组的函数,分别采用以下情况

显示隐式断言数组参数符合协议'MyFunctionalUtils',通过类型约束数组元素为'MyTypes'(函数bar1)。 显式显示数组参数符合协议'MyFunctionalUtils'(函数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

其他回答

(迅速2. x)

您还可以扩展数组以符合包含泛型类型方法的blue-rpints的协议,例如,包含符合某种类型约束的所有泛型数组元素的自定义函数utils的协议,例如协议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
    }
}

最后,测试和两个示例显示了接受泛型数组的函数,分别采用以下情况

显示隐式断言数组参数符合协议'MyFunctionalUtils',通过类型约束数组元素为'MyTypes'(函数bar1)。 显式显示数组参数符合协议'MyFunctionalUtils'(函数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

经过一段时间的尝试,解决方案似乎从签名中删除<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 { $0.compare("A") > 0 }

扩展所有类型:

extension Array where Element: Any {
    // ...
}

可比较的类型:

extension Array where Element: Comparable {
    // ...
}

扩展一些类型:

extension Array where Element: Comparable & Hashable {
    // ...
}

扩展一个特定类型:

extension Array where Element == Int {
    // ...
}

对于用类扩展类型化数组,下面的方法适合我(Swift 2.2)。例如,对类型化数组排序:

class HighScoreEntry {
    let score:Int
}

extension Array where Element == HighScoreEntry {
    func sort() -> [HighScoreEntry] {
      return sort { $0.score < $1.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 { $0 < $1 }
    }
}

在Swift 3中,一些类型被重命名:

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

如果你想了解扩展数组和其他类型的内置类的签出代码在这个github repo https://github.com/ankurp/Cent

从Xcode 6.1开始,扩展数组的语法如下所示

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