在Swift中扩展构造的genericstypes

是否有可能扩展一个generics类的专门/构造genericstypes? 我想用一个方法来扩展IntArrays来计算它的元素的总和。

例如

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

这可以使用协议扩展来实现(请参阅Swift编程语言:协议获取更多信息)。 在Swift 3:

总结一下Int你可以这样做:

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

用法:

 let nums = [1, 2, 3, 4] print(nums.sum) // Prints: "10" 

或者,对于更通用的东西,您可以使用@Wes Campaigne提出的build议,并创build一个Addable协议:

 protocol Addable { init() func + (lhs: Self, rhs: Self) -> Self } extension Int : Addable {} extension Double: Addable {} extension String: Addable {} ... 

接下来,扩展Sequence以添加可添加元素的序列:

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

用法:

 let doubles = [1.0, 2.0, 3.0, 4.0] print(doubles.sum) // Prints: "10.0" let strings = ["a", "b", "c"] print(strings.sum) // Prints: "abc" 

以可扩展的通用方式pipe理某些工作,而不会滥用types系统,但是它有一些限制。

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

具体来说:使用这种解决scheme,types推理不能用于无参数sum()方法,所以你必须注释预期的返回types或给它一个起始值(从中可以推断出types)。

另请注意,这将返回Optionaltypes的值:如果由于任何原因无法从数组计算预期types的​​总和,则返回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 

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

 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 } 

这是有点期待的,因为我们不能确定+操作符将被重载的所有可能的types,可以在一个数组中使用。

所以我们可以尝试仅限于某些类的操作。 就像是

 class Dummy { } extension Array { func someFunc<T:Dummy>() -> Int { return 0 } } var l = [Dummy()] var r = l.someFunc() // Expect 0 

从概念上说,这应该工作(目前似乎有一个错误,Xcode崩溃时使用此代码评估游乐场)。 在最终的工作中,我们不能使用这个技巧,因为typesInt不是一个类。

 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不是一个类使得它不可能。 如果数字types是类,如果我们可以有一个协议来定义一个类可以像ComparableEquatable一样添加,但是我的理解是协议不能定义创buildAddable协议所需的通用函数,那将会Addable

编辑:

正如其他答案所述,通过在闭包中明确地检查并转换为Int,可以使其适用于Int。 我想我错过了会调查。 但是,如果我们可以使用数字types的通用方法,那还是不错的。

在对sum()的inttypes进行testing之后,可以返回一个实数和值。 这样做,我会解决这个问题如下:

 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 

亚历山大,

你可以这样做:

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

就像在操场上testing的魅力一样。 但是,如果在不同types的数组上调用此函数,则可能会遇到麻烦。

你也可以做到这一点

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