一尘不染

我们如何创建通用数组扩展来汇总Swift中的数字类型?

swift

Swift可让您创建一个将Integer与之相加的Array扩展:

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

现在可以用来总结Int[]如下:

[1,2,3].sum() //6

但是我们如何制作一个通用的版本,该版本也支持对其他Number类型进行求和Double[]

[1.1,2.1,3.1].sum() //fails

这个问题 不是如何对数字求和 ,而是如何 创建通用数组扩展 来实现。


越来越近

如果可以帮助任何人更接近解决方案,这是我能够获得的最接近的结果:

您可以创建一个可以满足我们需要做的协议,即:

protocol Addable {
    func +(lhs: Self, rhs: Self) -> Self
    init()
}

然后扩展我们想要支持的符合上述协议的每种类型:

extension Int : Addable {
}

extension Double : Addable {
}

然后添加具有该约束的扩展:

extension Array {
    func sum<T : Addable>(min:T) -> T
    {
        return self.map { $0 as T }.reduce(min) { $0 + $1 }
    }
}

现在可以将其用于我们为支持该协议而扩展的数字,即:

[1,2,3].sum(0) //6
[1.1,2.1,3.1].sum(0.0) //6.3

不幸的是,我不得不在不提供参数的情况下使它运行,即:

func sum<T : Addable>(x:T...) -> T?
{
    return self.map { $0 as T }.reduce(T()) { $0 + $1 }
}

修改后的方法仍然可以使用1个参数:

[1,2,3].sum(0) //6

但是在不带参数的情况下无法解析该方法,即:

[1,2,3].sum() //Could not find member 'sum'

添加Integer到方法签名也无助于方法解析:

func sum<T where T : Integer, T: Addable>() -> T?
{
    return self.map { $0 as T }.reduce(T()) { $0 + $1 }
}

但是希望这将帮助其他人更接近解决方案。


一些进展

从@GabrielePetronella答案来看,如果我们在调用站点上明确指定类型,则可以调用上述方法:

let i:Int = [1,2,3].sum()
let d:Double = [1.1,2.2,3.3].sum()

阅读 264

收藏
2020-07-07

共1个答案

一尘不染

从Swift
2开始,可以使用协议扩展来做到这一点。(有关更多信息,请参见Swift编程语言:协议)。

首先,该Addable协议:

protocol Addable: IntegerLiteralConvertible {
    func + (lhs: Self, rhs: Self) -> Self
}

extension Int   : Addable {}
extension Double: Addable {}
// ...

接下来,扩展SequenceType以添加Addable元素序列:

extension SequenceType where Generator.Element: Addable {
    var sum: Generator.Element {
        return reduce(0, combine: +)
    }
}

用法:

let ints = [0, 1, 2, 3]
print(ints.sum) // Prints: "6"

let doubles = [0.0, 1.0, 2.0, 3.0]
print(doubles.sum) // Prints: "6.0"
2020-07-07