Swift允许您创建一个数组扩展,它将Integer与:
相加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 {
}
然后使用该约束添加扩展名:
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()
答案 0 :(得分:8)
我认为我找到了一种合理的方法,从scalaz借鉴了一些想法并从你提议的实施开始。 基本上我们想要的是拥有代表幺半群的类型类。
换句话说,我们需要:
这是一个建议的解决方案,它围绕快速类型系统限制
首先,我们友好的Addable
类型类
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
}
}
到目前为止一切顺利。现在我们已经拥有了构建通用`sum函数所需的所有部分:
extension Array {
func sum<T : Addable>() -> T {
return self.map { $0 as T }.reduce(T.zero()) { T.add($0, $1) }
}
}
让我们测试一下
let result: Int = [1,2,3].sum() // 6, yay!
由于类型系统的限制,您需要显式地转换结果类型,因为编译器无法自己确定Addable
解析为Int
。
所以你不能这样做:
let result = [1,2,3].sum()
我认为这种做法是一个可以忍受的缺点。
当然,这是完全通用的,它可以在任何类中使用,适用于任何类型的幺半群。
我之所以没有使用默认的+
运算符,而是定义add
函数,是因为这允许任何类型实现{{1类型类。如果你使用Addable
,那么没有定义+
运算符的类型,那么你需要在全局范围内实现这样的运算符,我不喜欢。
无论如何,如果您需要同时+
和Int
'可倍增',那么它是如何工作的,因为String
是为*
定义的,但不是对于`String。
Int
现在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()
数组可以使用方法String
来执行反向连接(只是一个愚蠢的例子),并且实现使用mult
运算符,为{{1}新定义}},而*
继续使用其通常的String
运算符,我们只需要为monoid定义一个零。
对于代码清洁,我更喜欢将整个类型类实现放在Int
范围内,但我想这是一个品味问题。
答案 1 :(得分:7)
从Swift 2开始,可以使用协议扩展来完成此操作。 (有关更多信息,请参阅The Swift Programming Language: Protocols)。
首先,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"
答案 2 :(得分:4)
在Swift 2中,您可以这样解决:
将monoid定义为添加为协议
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()方法:
extension Array where Element: Addable {
func sum() -> Element {
return self.reduce(Element.zero, combine: +)
}
}
答案 3 :(得分: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})
}
}
这很愚蠢,因为你必须说arr.sum(arr)
。换句话说,它不是封装的;它是一个免费的&#34;函数sum
恰好隐藏在Array中。因此,我未能解决您真正试图解决的问题。
答案 4 :(得分:0)
3> [1,2,3].reduce(0, +)
$R2: Int = 6
4> [1.1,2.1,3.1].reduce(0, +)
$R3: Double = 6.3000000000000007
答案 5 :(得分:0)
根据我对swift语法的理解,type identifier不能与泛型参数一起使用,只能用于泛型参数。因此,extension declaration只能用于具体类型。
答案 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 }
}
}
其他地方使用的Swift 2计划进行重大改进,包括异常处理,承诺和更好的通用元编程。
答案 7 :(得分:0)
帮助其他人努力将扩展名应用于所有Numeric
的值,而又不会显得混乱:
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
}
}
}