[英]Extension of constructed generic type in Swift
Is it possible to extend an generic class for a specialised/constructed generic type?是否可以为特殊/构造的泛型类型扩展泛型类? I would like to extend Int Arrays with a method to calculate the sum of its elements.
我想用一种方法来扩展 Int Arrays 来计算其元素的总和。
eg例如
extension Array<Int> {
func sum() -> Int {
return reduce(0) { $0 + $1 }
}
}
This can be achieved using protocol extensions (See The Swift Programming Language: Protocols for more information).这可以使用协议扩展来实现(有关更多信息,请参阅Swift 编程语言:协议)。 In Swift 3:
在 Swift 3 中:
To sum just Int
s you could do:总结只是
Int
你可以这样做:
extension Sequence where Iterator.Element == Int {
var sum: Int {
return reduce(0, +)
}
}
Usage:用法:
let nums = [1, 2, 3, 4]
print(nums.sum) // Prints: "10"
Or, for something more generic you could what @Wes Campaigne suggested and create an Addable
protocol:或者,对于更通用的内容,您可以按照@Wes Campaigne 的建议创建一个
Addable
协议:
protocol Addable {
init()
func + (lhs: Self, rhs: Self) -> Self
}
extension Int : Addable {}
extension Double: Addable {}
extension String: Addable {}
...
Next, extend Sequence
to add sequences of Addable
elements:接着,扩展
Sequence
添加的序列Addable
元素:
extension Sequence where Iterator.Element: Addable {
var sum: Iterator.Element {
return reduce(Iterator.Element(), +)
}
}
Usage:用法:
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"
Managed to get something working in an extensible, generic fashion without abusing the type system too badly, however it has some limitations.设法使某些东西以可扩展的通用方式工作,而不会严重滥用类型系统,但是它有一些限制。
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
}
}
}
}
Specifically: with this solution, type inferencing won't work on the no-parameter sum()
method, so you have to either annotate the expected return type or give it a starting value (from which it can infer the type).具体来说:使用此解决方案,类型推断不适用于无参数
sum()
方法,因此您必须注释预期的返回类型或为其提供一个起始值(从中可以推断类型)。
Note also that this returns a value of Optional type: if for any reason a sum of the expected type cannot be computed from the array, it returns nil.还要注意,这会返回一个 Optional 类型的值:如果由于某种原因无法从数组中计算出预期类型的总和,则返回 nil。
To illustrate:为了显示:
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
Swift 5.x:斯威夫特 5.x:
extension Array where Element == Int {
var sum: Int {
reduce(0, +)
}
}
Looks like you can't.好像不能The closest we can get is the function
我们能得到的最接近的是函数
func sum(a:Array<Int>) -> Int {
return a.reduce(0) {$0 + $1}
}
Swift will allow you to add extension on the Array class but not specifically to a specialized version of the class. Swift 将允许您在 Array 类上添加扩展,但不会专门添加到该类的专门版本。
error: <REPL>:108:1: error: non-nominal type 'Array<Int>' cannot be extended
You can extend the Array class.您可以扩展 Array 类。
extension Array {
func sum() -> Int {
return reduce(0) { $0 + $1 }
}
}
The problem is now with the +
operator现在的问题是
+
运算符
error: <REPL>:102:16: error: could not find an overload for '+' that accepts the supplied arguments
return reduce(0) { $0 + $1 }
This is somewhat expected since we cannot be sure that the +
operator will be will be overloaded for all the possible types that could be used in an array.这在一定程度上是预料之中的,因为我们不能确定
+
运算符将被重载用于数组中所有可能的类型。
So we could try to constraint the operation only on certain classes.所以我们可以尝试将操作限制在某些类上。 Something like
就像是
class Dummy {
}
extension Array {
func someFunc<T:Dummy>() -> Int {
return 0
}
}
var l = [Dummy()]
var r = l.someFunc() // Expect 0
Conceptually this should work (currently it seems that there is a bug, Xcode crashes when evaluating a playground using this code).从概念上讲,这应该可行(目前似乎存在一个错误,Xcode 在使用此代码评估游乐场时崩溃)。 In the eventually that it works, we cannot use this trick since the type
Int
is not a class.最后它起作用了,我们不能使用这个技巧,因为类型
Int
不是一个类。
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 {
I also looked at extending the Array class with a protocol but again Int
not being a class makes it impossible.我还研究了使用协议扩展 Array 类,但再次
Int
不是一个类使得它不可能。 If the numeric types were classes, it would be nice if we could have a protocol to define that a class can be added just like Comparable
or Equatable
but my understanding is that protocol cannot define generic function which would be needed to create a Addable
protocol.如果数字类型是类,那么如果我们可以有一个协议来定义一个类可以像
Comparable
或Equatable
一样添加,那就Equatable
但我的理解是协议不能定义创建可Addable
协议所需的通用函数。
Edit:编辑:
As stated by other answers, you can make it work for Int by explicitly checking and casting to Int in the closure.正如其他答案所述,您可以通过在闭包中显式检查并转换为 Int 来使其适用于 Int 。 I guess I missed it will investigating.
我想我错过了它会调查。 But it would still be nice if we could have a generic way of working with numeric types.
但是如果我们能有一种通用的方法来处理数字类型,那还是很好的。
It is possible to return a real sum-value after you have tested for the int-type in sum()
.在
sum()
测试 int 类型后,可以返回一个真实的 sum 值。 Doing so I would solve the problem as follows:这样做我将解决以下问题:
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
Alexander,亚历山大,
Here's how you can do it:您可以这样做:
extension Array {
func sum() -> Int {
return reduce(0) { ($0 as Int) + ($1 as Int) }
}
}
Works like a charm, tested in the playground.就像一个魅力,在操场上测试。 However, you might get into trouble if you call this function on different types of arrays.
但是,如果您在不同类型的数组上调用此函数,您可能会遇到麻烦。
There is a built in protocol, AdditiveArithmetic
, that defines the generic +
operator, so we can write an extension that works for all 'Addable' types automatically.有一个内置协议
AdditiveArithmetic
,它定义了泛型+
运算符,因此我们可以编写一个自动适用于所有“可添加”类型的扩展。 This is the example given on Apple's documentation page:这是 Apple 文档页面上给出的示例:
extension Sequence where Element: AdditiveArithmetic {
func sum() -> Element {
return reduce(.zero, +)
}
}
This will now work on any Sequence
or Collection
with any kind of 'Addable' elements, including Int
, Double
, Float
etc.现在,这将适用于具有任何类型的“可添加”元素的任何
Sequence
或Collection
,包括Int
、 Double
、 Float
等。
you can do it as well你也可以做到
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
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.