Swift允许您创建一个数组扩展,它将Integer与:
extension Array {
func sum() -> Int {
return self.map { $0 as Int }.reduce(0) { $0 + $1 }
}
}
Run Code Online (Sandbox Code Playgroud)
现在可以用来总结Int[]
如下:
[1,2,3].sum() //6
Run Code Online (Sandbox Code Playgroud)
但是我们如何制作一个支持对其他数字类型求和的通用版本Double[]
呢?
[1.1,2.1,3.1].sum() //fails
Run Code Online (Sandbox Code Playgroud)
这个问题不是如何对数字求和,而是如何创建一个通用的数组扩展来实现它.
如果能帮助任何人更接近解决方案,这是我能够获得的最接近的:
您可以创建一个可以满足我们需要的协议,即:
protocol Addable {
func +(lhs: Self, rhs: Self) -> Self
init()
}
Run Code Online (Sandbox Code Playgroud)
然后扩展我们想要支持的每种类型,以符合上述协议:
extension Int : Addable {
}
extension Double : Addable {
}
Run Code Online (Sandbox Code Playgroud)
然后添加具有该约束的扩展:
extension Array {
func sum<T : Addable>(min:T) -> T
{
return self.map { $0 as T }.reduce(min) { $0 + $1 }
}
}
Run Code Online (Sandbox Code Playgroud)
现在可以用来对付我们扩展为支持协议的数字,即:
[1,2,3].sum(0) //6
[1.1,2.1,3.1].sum(0.0) //6.3
Run Code Online (Sandbox Code Playgroud)
不幸的是,我无法在不提供参数的情况下使其工作,即:
func sum<T : Addable>(x:T...) -> T?
{
return self.map { $0 as T }.reduce(T()) { $0 + $1 }
}
Run Code Online (Sandbox Code Playgroud)
修改后的方法仍然适用于1个参数:
[1,2,3].sum(0) //6
Run Code Online (Sandbox Code Playgroud)
但是在没有参数的情况下调用它时无法解析方法,即:
[1,2,3].sum() //Could not find member 'sum'
Run Code Online (Sandbox Code Playgroud)
添加Integer
到方法签名也无法帮助解决方法:
func sum<T where T : Integer, T: Addable>() -> T?
{
return self.map { $0 as T }.reduce(T()) { $0 + $1 }
}
Run Code Online (Sandbox Code Playgroud)
但希望这将有助于其他人更接近解决方案.
从@GabrielePetronella回答,如果我们在调用网站上明确指定类型,我们可以调用上面的方法,如:
let i:Int = [1,2,3].sum()
let d:Double = [1.1,2.2,3.3].sum()
Run Code Online (Sandbox Code Playgroud)
我想我找到了一种合理的方法,从scalaz中借鉴一些想法并从你提议的实现开始.基本上我们想要的是具有代表幺半群的类型类.
换句话说,我们需要:
这是一个建议的解决方案,它围绕快速类型系统限制
首先,我们友好的Addable
类型类
protocol Addable {
class func add(lhs: Self, _ rhs: Self) -> Self
class func zero() -> Self
}
Run Code Online (Sandbox Code Playgroud)
现在让我们来Int
实现吧.
extension Int: Addable {
static func add(lhs: Int, _ rhs: Int) -> Int {
return lhs + rhs
}
static func zero() -> Int {
return 0
}
}
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.现在我们已经拥有了构建通用`sum函数所需的所有部分:
extension Array {
func sum<T : Addable>() -> T {
return self.map { $0 as T }.reduce(T.zero()) { T.add($0, $1) }
}
}
Run Code Online (Sandbox Code Playgroud)
我们来试试吧
let result: Int = [1,2,3].sum() // 6, yay!
Run Code Online (Sandbox Code Playgroud)
由于类型系统的限制,您需要显式地转换结果类型,因为编译器无法自行Addable
解析解析Int
.
所以你不能只做:
let result = [1,2,3].sum()
Run Code Online (Sandbox Code Playgroud)
我认为这种方法是一个可以忍受的缺点.
当然,这是完全通用的,它可以用于任何类,任何类型的monoid.我之所以没有使用默认+
运算符,而是我定义了一个add
函数,是因为它允许任何类型实现Addable
类型类.如果你使用+
,那么一个没有+
运算符定义的类型,那么你需要在全局范围内实现这样的运算符,我有点不喜欢.
无论如何,如果你需要例如同时生成' Int
和String
'multipliable',它的工作原理*
是这样的,因为它是Int
为`String而定义的.
protocol Multipliable {
func *(lhs: Self, rhs: Self) -> Self
class func m_zero() -> Self
}
func *(lhs: String, rhs: String) -> String {
return rhs + lhs
}
extension String: Multipliable {
static func m_zero() -> String {
return ""
}
}
extension Int: Multipliable {
static func m_zero() -> Int {
return 1
}
}
extension Array {
func mult<T: Multipliable>() -> T {
return self.map { $0 as T }.reduce(T.m_zero()) { $0 * $1 }
}
}
let y: String = ["hello", " ", "world"].mult()
Run Code Online (Sandbox Code Playgroud)
现在数组String
可以使用该方法mult
执行反向连接(只是一个愚蠢的例子),并且实现使用*
新定义的运算符String
,而Int
保持使用其常用*
运算符,我们只需要为monoid定义零.
对于代码清洁,我更喜欢将整个类型类实现放在extension
范围内,但我想这是一个品味问题.
从Swift 2开始,可以使用协议扩展来完成此操作.(有关更多信息,请参阅Swift编程语言:协议).
首先,Addable
协议:
protocol Addable: IntegerLiteralConvertible {
func + (lhs: Self, rhs: Self) -> Self
}
extension Int : Addable {}
extension Double: Addable {}
// ...
Run Code Online (Sandbox Code Playgroud)
接下来,扩展SequenceType
以添加Addable
元素序列:
extension SequenceType where Generator.Element: Addable {
var sum: Generator.Element {
return reduce(0, combine: +)
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
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"
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
9322 次 |
最近记录: |