Swift中构造泛型类型的扩展

时间:2014-06-04 20:41:06

标签: generics swift

是否可以为专用/构造的泛型类型扩展泛型类?我想用一种方法扩展Int Arrays以计算其元素的总和。

e.g。

extension Array<Int> {

    func sum() -> Int {
        return reduce(0) { $0 + $1 }
    }

}

8 个答案:

答案 0 :(得分:46)

这可以使用协议扩展来实现(有关详细信息,请参阅The Swift Programming Language: Protocols)。在Swift 3中:

总结nRowCount你可以做的事情:

Int

用法:

extension Sequence where Iterator.Element == Int {
    var sum: Int {
        return reduce(0, +)
    }
}

或者,对于更通用的内容,您可以@Wes Campaigne建议并创建let nums = [1, 2, 3, 4] print(nums.sum) // Prints: "10" 协议:

Addable

接下来,扩展protocol Addable { init() func + (lhs: Self, rhs: Self) -> Self } extension Int : Addable {} extension Double: Addable {} extension String: Addable {} ... 以添加Sequence元素的序列:

Addable

用法:

extension Sequence where Iterator.Element: Addable {
    var sum: Iterator.Element {
        return reduce(Iterator.Element(), +)
    }
}

答案 1 :(得分:8)

管理以便以可扩展的通用方式工作而不会过度滥用类型系统,但它有一些限制。

protocol Addable {
    func +(lhs: Self, rhs: Self) -> Self
    class var identity: Self { get }
}

extension Int : Addable {
    static var identity: Int { get { return 0 } }
}

extension String : Addable {
    static var identity: String { get { return "" } }
}

extension Array {
    func sum<U : Addable>() -> U? {
        let s: U? = U.identity
        return self.sum(s)
    }

    func sum<U : Addable>(start: U?) -> U? {
        return reduce(start) { lhs, rhs in
            switch (lhs, rhs) {
            case (.Some(let left), let right as U):
                return left + right
            default:
                return nil
            }
        }
    }
}

具体来说:使用此解决方案,类型推断将无法在无参数sum()方法上工作,因此您必须注释预期的返回类型或为其指定一个起始值(从中可以推断出类型)。

另请注意,这将返回Optional类型的值:如果由于任何原因无法从数组计算预期类型的​​总和,则返回nil。

举例说明:

let int_array = Array(1...10)

let x: Int? = int_array.sum() // result: {Some 55}
let x2 = int_array.sum(0) // result: {Some 55}
let x3 = int_array.sum() // Compiler error because it can't infer type


let string_array = ["a", "b", "c"]

let y: String? = string_array.sum() // result: {Some "abc"}
let y2 = string_array.sum("") // result: {Some "abc"}

let y3: Int? = string_array.sum() // result: nil  (can't cast String to Int)
let y4 = string_array.sum(0) // result: nil  (can't cast String to Int)


let double_array = [1.3, 4.2, 2.1]

let z = double_array.sum(0.0) // Compiler error because we haven't extended Double to be Addable

答案 2 :(得分:4)

Swift 5.x:

extension Array where Element == Int {

    var sum: Int {
        reduce(0, +)
    }
}

答案 3 :(得分:3)

看起来你不能。我们能得到的最接近的是函数

func sum(a:Array<Int>) -> Int {
    return a.reduce(0) {$0 + $1}
}

Swift允许您在Array类上添加扩展名,但不能专门添加到该类的专用版本。

error: <REPL>:108:1: error: non-nominal type 'Array<Int>' cannot be extended

您可以扩展Array类。

extension Array {

    func sum() -> Int {
        return reduce(0) { $0 + $1 }
    }
}

现在问题在于+运算符

error: <REPL>:102:16: error: could not find an overload for '+' that accepts the supplied arguments
        return reduce(0) { $0 + $1 }

这是有点预期的,因为我们无法确定+运算符是否会因为数组中可能使用的所有可能类型而被重载。

因此我们可以尝试仅对某些类约束操作。像

这样的东西
class Dummy {
}

extension Array {
    func someFunc<T:Dummy>() -> Int {
       return 0
    }
}

var l = [Dummy()]
var r = l.someFunc() // Expect 0

从概念上讲,这应该可行(目前似乎存在一个错误,Xcode在使用此代码评估游乐场时崩溃)。在最终它起作用的时候,我们不能使用这个技巧,因为类型Int不是类。

extension Array {
    func sum<T:Int>() -> T {
        return reduce(0) { $0 + $1 }
    }
}

error: <REPL>:101:14: error: inheritance from non-protocol, non-class type 'Int'
    func sum<T:Int>() -> T {

我还考虑使用协议扩展Array类,但再次Int不是类,这使得它无法实现。如果数字类型是类,那么如果我们可以有一个协议来定义一个类可以像ComparableEquatable那样添加,那将是很好的但我的理解是协议不能定义通用函数需要创建Addable协议。

编辑:

正如其他答案所述,您可以通过在闭包中显式检查并转换为Int来使其适用于Int。我想我错过了它会调查。但是,如果我们能够使用数字类型的通用方法,那仍然会很好。

答案 4 :(得分:2)

亚历山大,

以下是如何做到的:

extension Array {
    func sum() -> Int {
        return reduce(0) { ($0 as Int) + ($1 as Int) }
    }
}

像在运动场测试的魅力一样。但是,如果在不同类型的数组上调用此函数,则可能会遇到麻烦。

答案 5 :(得分:1)

sum()中测试int类型后,可以返回实数和值。这样做我会解决问题如下:

import Cocoa

extension Array {
    func sum() -> Int {
        if !(self[0] is Int) { return 0; }
        var sum = 0;
        for value in self { sum += value as Int }
        return sum;
    }
}

let array = [1,2,3,4,5]
array.sum() // =15

let otherArray = ["StringValue"]
otherArray.sum() // =0

答案 6 :(得分:0)

Swift 5.x

有一个内置协议 AdditiveArithmetic,它定义了通用的 + 运算符,因此我们可以编写一个自动适用于所有“可添加”类型的扩展。这是 Apple 文档页面上给出的示例:

extension Sequence where Element: AdditiveArithmetic {
    func sum() -> Element {
        return reduce(.zero, +)
    }
}

这现在适用于具有任何类型的“可添加”元素的任何 SequenceCollection,包括 IntDoubleFloat 等。< /p>

答案 7 :(得分:-1)

你也可以这样做

extension Array {
    func sum () -> Int? {
        guard self.count > 0  && self.first is Int  else {
            return nil
        }
        var s = 0
        forEach {
            s += $0 as! Int
        }
        return s
    }
}