Reputation: 59
I wrote a function in order to get the standard deviation from a
array of floats, but I'm have a problem, how can I use it
if I have a array of ints?
I dont want to have a function for every data type...
func StdDev(a []float64) float64 {
var Prom float64
sum := 0.0
Total := 0.0
n := len(a)
N := float64(n)
for i := 0; i < n; i++ {
sum += a[i]
}
Prom = sum / N
for i := 0; i < n; i++ {
Total += (a[i] - Prom) * (a[i] - Prom)
}
Total = Total / N
Total = math.Sqrt(Total)
return Total
}
Upvotes: 5
Views: 3424
Reputation: 12826
As of August 20, 2021, GoLang has a Type Parameters Proposal to add parametric polymorphism, or generics, to GoLang.
We currently expect that this change will be available in the Go 1.18 release in early 2022
Under that proposal, the capabilities of interfaces will be expanded to include Type Sets, which can Union different types.
The proposal gives an example of doubling a list of numeric elements, which should help clarify how parametric polymorphism can solve problems like the StdDev
one:
// Double returns a new slice that contains all the elements of s, doubled.
func Double[E constraints.Number](s []E) []E {
r := make([]E, len(s))
for i, v := range s {
r[i] = v + v
}
return r
}
There is a discussion on the constraint package API here, and the source can be viewed here.
There does not appear to be a constraints.Number
or constraints.Numeric
, so you would need to make one, but that is simple:
type RealNumber interface {
Integer | Float
}
And then the syntax of your function should be straightforward:
func StdDev[E RealNumber](data []E) float64 {
// determine the mean
sum := 0.0
for _, datum := range data {
sum += datum
}
mean := sum / len(a)
// calculate the variance
sum = 0.0
for _, datum := range data {
deviation := datum - mean
sum += deviation * deviation
}
variance := sum / len(a)
return math.Sqrt(variation)
}
Upvotes: 0
Reputation: 2129
You can use interfaces, just like the sort package does:
http://play.golang.org/p/4N_UpFScoU
package main
import "math"
type Adder interface {
Add(a float64) float64
}
type floatAdder float64
func (f floatAdder) Add(a float64) float64 {
return float64(f) + a
}
type intAdder int
func (i intAdder) Add(a float64) float64 {
return float64(i) + a
}
func StdDev(a []Adder) float64 {
var Prom float64
sum := 0.0
Total := 0.0
n := len(a)
N := float64(n)
for i := 0; i < n; i++ {
sum = a[i].Add(sum)
}
Prom = sum / N
for i := 0; i < n; i++ {
Total += a[i].Add(-Prom) * a[i].Add(-Prom)
}
Total = Total / N
Total = math.Sqrt(Total)
return Total
}
func main() {
floats := []Adder{floatAdder(1.0), floatAdder(2.0), floatAdder(3.0)}
println(StdDev(floats))
ints := []Adder{intAdder(1), intAdder(2), intAdder(3)}
println(StdDev(ints))
}
Upvotes: 2
Reputation: 57639
Go has no generics, so you are not able to write a solution that covers []int
and []float64
at the same time. You have to copy the values from your []int
to a []float64
using a simple for-loop and a type conversion from int
to float
. Then you can use your function. Example (play):
a := []int{1,2,3,4}
b := make([]float64, len(a))
for i := range a {
b[i] = float64(a[i])
}
StdDev(b)
What you can also do is to write a function based on reflection values and then use reflect.MakeFunc
. This would be slower, harder to make and more code to write so the benefit is questionable.
Although this is off-topic I can't help but noticing that your code may look nicer if you would use range clauses for your loops. Also, variables in a function body are written in lower caps. There are some other syntactic tricks such as named return values. Utilizing these features of Go, your code may look nicer:
func Avg(a []float64) (sum float64) {
for i := range a {
sum += a[i]
}
return sum / float64(len(a))
}
func StdDev(a []float64) (total float64) {
prom := Avg(a)
for i := range a {
total += (a[i] - prom) * (a[i] - prom)
}
total = total / float64(len(a))
return math.Sqrt(total)
}
Upvotes: 4