如何在Swift中创建一个对数字类型求和的通用数组扩展?
Swift允许您创建一个数组扩展名,该扩展名将整数与:如何在Swift中创建一个对数字类型求和的通用数组扩展?,swift,Swift,Swift允许您创建一个数组扩展名,该扩展名将整数与: extension Array { func sum() -> Int { return self.map { $0 as Int }.reduce(0) { $0 + $1 } } } 现在可用于求和Int[]如下: [1,2,3].sum() //6 但是,我们如何制作一个通用版本来支持对其他类型的数字进行求和,比如Double[] [1.1,2.1,3.1].sum() //fails 这
extension Array {
func sum() -> Int {
return self.map { $0 as Int }.reduce(0) { $0 + $1 }
}
}
现在可用于求和Int[]
如下:
[1,2,3].sum() //6
但是,我们如何制作一个通用版本来支持对其他类型的数字进行求和,比如Double[]
[1.1,2.1,3.1].sum() //fails
这个问题不是如何求和,而是如何创建一个通用数组扩展
越来越近 如果它能帮助任何人更接近解决方案,这是我所能得到的最接近的结果: 您可以创建一个能够满足我们需要的协议,即:
protocol Addable {
func +(lhs: Self, rhs: Self) -> Self
init()
}
然后扩展我们要支持的符合上述协议的每种类型:
extension Int : Addable {
}
extension Double : Addable {
}
protocol Addable: IntegerLiteralConvertible {
func + (lhs: Self, rhs: Self) -> Self
}
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 }
}
}
不幸的是,我无法在不提供参数的情况下使其工作,即:
func sum<T : Addable>(x:T...) -> T?
{
return self.map { $0 as T }.reduce(T()) { $0 + $1 }
}
但是在没有参数的情况下调用该方法时无法解析该方法,即:
[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 }
}
下面是一个愚蠢的实现:
extension Array {
func sum(arr:Array<Int>) -> Int {
return arr.reduce(0, {(e1:Int, e2:Int) -> Int in return e1 + e2})
}
func sum(arr:Array<Double>) -> Double {
return arr.reduce(0, {(e1:Double, e2:Double) -> Double in return e1 + e2})
}
}
扩展数组{
func sum(arr:Array)->Int{
返回arr.reduce(0,{(e1:Int,e2:Int)->返回e1+e2}中的Int)
}
func sum(arr:Array)->Double{
返回arr.reduce(0,{(e1:Double,e2:Double)->返回e1+e2}中的Double)
}
}
这很愚蠢,因为你必须说
arr.sum(arr)
。换句话说,它不是封装的;它是一个“自由”函数sum
,恰好隐藏在数组中。因此,我未能解决您真正想要解决的问题。根据我对swift语法的理解,a不能与泛型参数一起使用,只能与泛型参数一起使用。因此,只能与具体类型一起使用。我认为我找到了一种合理的方法,从您建议的实现中借鉴了一些想法并从中开始。
基本上我们想要的是有表示幺半群的类型类
换言之,我们需要:
- 结合函数
- 标识值(即零)
Addable
typeclass
protocol Addable {
class func add(lhs: Self, _ rhs: Self) -> Self
class func zero() -> Self
}
现在让我们让Int
实现它
extension Int: Addable {
static func add(lhs: Int, _ rhs: Int) -> Int {
return lhs + rhs
}
static func zero() -> Int {
return 0
}
}
到目前为止还不错。现在我们有了构建通用“求和函数”所需的所有部分:
extension Array {
func sum<T : Addable>() -> T {
return self.map { $0 as T }.reduce(T.zero()) { T.add($0, $1) }
}
}
由于类型系统的限制,您需要显式强制转换结果类型,因为编译器本身无法将Addable
解析为Int
所以你不能只做:
let result = [1,2,3].sum()
我认为这是这种方法的一个可以忍受的缺点
当然,这是完全通用的,它可以用于任何类,任何类型的幺半群。
我没有使用默认的+
操作符,而是定义了一个add
函数,原因是它允许任何类型实现可添加的类型类。如果您使用+
,那么类型没有定义+
操作符,那么您需要在全局范围内实现这样的操作符,我有点不喜欢
无论如何,如果您需要将Int
和String
都设置为“可倍增”,那么下面是它的工作方式,因为*
是为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()
协议可复用{
func*(左:自,右:自)->Self
类func m_zero()->Self
}
func*(左:字符串,右:字符串)->String{
返回右+左
}
扩展字符串:可乘法{
静态函数m_zero()->字符串{
返回“”
}
}
扩展名Int:可乘法{
静态函数m_zero()->Int{
返回1
}
}
扩展阵列{
func mult()->T{
返回self.map{$0作为T}.reduce(T.m_zero()){$0*$1}
}
}
让y:String=[“你好”,“世界”]。mult()
现在,String
数组可以使用方法mult
来执行反向串联(这只是一个愚蠢的例子),实现使用新定义的*
操作符String
,然而,Int
继续使用它通常的*
操作符,我们只需要为幺半群定义一个零
对于代码清洁,我更喜欢将整个typeclass实现放在扩展范围内,但我想这是一个品味问题。从Swift 2开始,使用协议扩展就可以做到这一点。(有关更多信息,请参阅)
首先,可添加的协议:
extension Int : Addable {
}
extension Double : 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: +)
}
}
extension Array where Element: Addable {
func sum() -> Element {
return self.reduce(Element.zero, 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"
基于Swift 1.x中先前的答案,它是可行的,只需付出最小的努力:
import Foundation
protocol Addable {
func +(lhs: Self, rhs: Self) -> Self
init(_: Int)
init()
}
extension Int : Addable {}
extension Int8 : Addable {}
extension Int16 : Addable {}
extension Int32 : Addable {}
extension Int64 : Addable {}
extension UInt : Addable {}
extension UInt8 : Addable {}
extension UInt16 : Addable {}
extension UInt32 : Addable {}
extension UInt64 : Addable {}
extension Double : Addable {}
extension Float : Addable {}
extension Float80 : Addable {}
// NSNumber is a messy, fat class for ObjC to box non-NSObject values
// Bit is weird
extension Array {
func sum<T : Addable>(min: T = T(0)) -> T {
return map { $0 as! T }.reduce(min) { $0 + $1 }
}
}
<代码>导入基础
协议可添加{
func+(左:自,右:自)->Self
初始化
init()
}
扩展名Int:Addable{}
扩展名Int8:可添加{}
扩展名Int16:可添加{}
扩展名Int32:可添加{}
扩展名Int64:可添加{}
扩展UInt:可添加{}
扩展UInt8:可添加{}
扩展UInt16:可添加{}
扩展UInt32:可添加{}
扩展UInt64:可添加{}
双扩展名:可添加{}
扩展浮点:可添加{}
扩展浮点80:可添加{}
//NSNumber是一个凌乱的、胖的类,用于ObjC框装非NSObject值
//比特很奇怪
扩展阵列{
函数和(最小值:T=T(0))->T{
返回映射{$0as!T}.reduce(min){$0+$1}
}
}
在这里:
其他地方使用的Swift 2计划进行重大改进,包括异常处理、承诺和更好的通用元编程。在Swift 2中,您可以这样解决它:
将加法的幺半群定义为协议
protocol Addable {
init()
func +(lhs: Self, rhs: Self) -> Self
static var zero: Self { get }
}
extension Addable {
static var zero: Self { return Self() }
}
除了其他解决方案外,这还使用标准初始值设定项显式定义了零元素
然后将Int和Double声明为可添加:
extension Int: Addable {}
extension Double: Addable {}
现在可以为所有数组定义sum()方法
protocol Addable {
init()
func +(lhs: Self, rhs: Self) -> Self
static var zero: Self { get }
}
extension Addable {
static var zero: Self { return Self() }
}
extension Int: Addable {}
extension Double: Addable {}
extension Array where Element: Addable {
func sum() -> Element {
return self.reduce(Element.zero, combine: +)
}
}
extension Numeric where Self: Comparable {
/// Limits a numerical value.
///
/// - Parameter range: The range the value is limited to be in.
/// - Returns: The numerical value clipped to the range.
func limit(to range: ClosedRange<Self>) -> Self {
if self < range.lowerBound {
return range.lowerBound
} else if self > range.upperBound {
return range.upperBound
} else {
return self
}
}
}