Reputation: 583
The propose is to calculate incrementally the average The code below is the best way I found to calculate incremental average, in order to use it for big numbers and or great array
The following is an example give this array doubles
let values = [14.0,12.0, 23.4,37.5,11.46]
var index = 1
let avg = values.reduce(0.0) { return $0 + ( $1 - $0 ) / Double(index++) }
avg will be 19.672. and it works.
Is it correct from your point of view?
Is there a way in order to accomplish this with something like:
let avg = values.averageIncr()
What I don't like is that I have to use and index?
[updated]
a step further, thanks to @Martin R contribute
protocol NumericType:Equatable {
func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
init(_ value : Int)
}
extension Double : NumericType { }
extension Array where Element : NumericType {
mutating func avg_inc_adding( element: Element, var startAvg:Element? = Element(0) ) throws -> Element{
if count > 0 && startAvg! == Element(0) {
startAvg = self.avg_inc()
}
append(element)
return startAvg! + (element - startAvg!) / Element(self.count - 1)
}
func avg_inc() -> Element {
return enumerate().reduce(Element(0)) { $0 + ( $1.1 - $0 ) / Element($1.0 + 1) }
}
}
in this way I'm be able to do something like:
var average:Double = values.avg_inc()
do {
average = try values.avg_inc_adding(34.6,startAvg: z)
}catch {
}
that fit with my needs, and I hope with the ones for somebody else.
Upvotes: 3
Views: 952
Reputation: 539965
You get the same result without the need for an "external variable" with
let avg = values.enumerate().reduce(0.0) { $0 + ( $1.1 - $0 ) / Double($1.0 + 1) }
because enumerate()
returns a sequence of index/element pairs.
Implementing that as an extension method is a bit complicated but possible:
protocol NumericType {
func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
init(_ value : Int)
}
extension Double : NumericType { }
extension Array where Element : NumericType {
func averageIncr() -> Element {
return enumerate().reduce(Element(0)) { $0 + ( $1.1 - $0 ) / Element($1.0 + 1) }
}
}
let avg = values.averageIncr()
The Element
type in an Array
extension can only restricted to
a protocol, not a type. Similarly as in e.g. What protocol should be adopted by a Type for a generic function to take any number type as an argument in Swift? or
How can we create a generic Array Extension that sums Number types in Swift?, you have to define a
protocol which contains all the methods needed in your calculation.
The restriction Element: FloatingPointType
is not sufficient because
the FloatingPointType
protocol does not define any arithmetic operations (+, -, /).
Update for Swift 3: As of Swift 3/Xcode 8, floating point
types conform to the FloatPoint
protocol and that defines the
arithmetic operations (+, -, /). Therefore a custom protocol is
no longer necessary:
extension Array where Element: FloatingPoint {
func averageIncr() -> Element {
return enumerated().reduce(Element(0)) { $0 + ( $1.1 - $0 ) / Element($1.0 + 1) }
}
}
Upvotes: 3