英文:
Golang Parametric polymorphism?
问题
我写了一个函数来计算浮点数数组的标准差,但是我遇到了一个问题,如果我有一个整数数组,我该如何使用它呢?
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
}
我不想为每种数据类型编写一个函数。
英文:
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?<br>
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
}
答案1
得分: 4
Go语言没有泛型,因此无法同时处理[]int
和[]float64
。你需要使用一个简单的for循环和类型转换,将[]int
中的值复制到[]float64
中。然后你就可以使用你的函数了。示例代码如下:
a := []int{1,2,3,4}
b := make([]float64, len(a))
for i := range a {
b[i] = float64(a[i])
}
StdDev(b)
你还可以基于反射值编写一个函数,然后使用reflect.MakeFunc
来使用。这种方法会更慢,更难实现,并且需要编写更多的代码,所以效益是可疑的。
代码风格方面,虽然这是离题的,但我还是注意到你的代码可能会更好看一些,如果你在循环中使用range子句。此外,函数体中的变量应该使用小写字母。还有一些其他的语法技巧,比如命名返回值。利用Go语言的这些特性,你的代码可能会更加优雅:
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)
}
英文:
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.
Code style
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)
}
答案2
得分: 2
你可以使用接口,就像sor包一样:
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))
}
这段代码定义了一个接口Adder
,它有一个Add
方法。然后定义了两个类型floatAdder
和intAdder
,它们分别实现了Add
方法。StdDev
函数接受一个Adder
类型的切片,并计算标准差。在main
函数中,分别使用floatAdder
和intAdder
类型的切片调用StdDev
函数,并打印结果。
英文:
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))
}
答案3
得分: 0
截至2021年8月20日,Go语言有一个类型参数提案,旨在为Go语言添加参数多态性或泛型。
> 我们目前预计这个变化将在2022年初的Go 1.18版本中可用。
根据该提案,接口的功能将扩展以包括类型集,可以合并不同的类型。
该提案提供了一个示例,用于将数字元素的列表加倍,这应该有助于澄清参数多态性如何解决类似StdDev
的问题:
// Double返回一个包含s中所有元素加倍的新切片。
func Double[E constraints.Number](s []E) []E {
r := make([]E, len(s))
for i, v := range s {
r[i] = v + v
}
return r
}
关于约束包API的讨论可以在这里找到,源代码可以在这里查看。
似乎没有constraints.Number
或constraints.Numeric
,所以你需要创建一个,但这很简单:
type RealNumber interface {
Integer | Float
}
然后你的函数的语法应该很简单:
func StdDev[E RealNumber](data []E) float64 {
// 计算平均值
sum := 0.0
for _, datum := range data {
sum += datum
}
mean := sum / len(a)
// 计算方差
sum = 0.0
for _, datum := range data {
deviation := datum - mean
sum += deviation * deviation
}
variance := sum / len(a)
return math.Sqrt(variation)
}
英文:
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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论