[英]Confusion with type inference of literals in Go
我已经开始学习GoLang,目前正在阅读有关使用短变量声明语法的类型推断系统的信息。
这是一个简单的程序,引起了我的注意,并且使我难以理解:
package main
import (
"fmt"
"sort"
)
type statistics struct {
numbers []float64
mean float64
median float64
}
// Performs analytics on a slice of floating-point numbers
func GenerateStats(numbers []float64) (stats statistics) {
stats.numbers = numbers
stats.mean = sum(numbers) / float64(len(numbers))
sort.Float64s(numbers)
stats.median = median(numbers)
return stats
}
// Helper function to sum up a slice of floats
func sum(numbers []float64) (total float64) {
for _, num := range numbers {
total += num
}
return total
}
// Helper to find the median of a sorted slice of floats
func median(numbers []float64) (med float64) {
n := len(numbers)
if n%2 == 0 {
med = numbers[n/2]
} else {
med = (numbers[n/2] + numbers[(n-1)/2]) / 2 // Infer 01
}
return med
}
func main() {
nums := []float64{1, 2, 3, 3, 4}
result := generateStats(nums)
fmt.Println(result.numbers)
fmt.Println(result.mean)
fmt.Println(result.median)
b := "This is Go" + 1.9 // Infer 02
fmt.Println(b)
}
当我使用以下命令执行该程序时: $ go run <path>/statistics.go
,
我收到以下错误消息:
# command-line-arguments
<path>/statistics.go:47:20: cannot convert "This is Go" to type float64
<path>/statistics.go:47:20: invalid operation: "This is Go" + 1.9 (mismatched types string and float64)
这是行为不同的原因:
Infer 01
:数字文字2
的类型是根据其使用的表达式来推断的。由于分子的类型为float64
,因此Go假定对应类型的2
为成功进行除法。 因此,LHS上的变量类型为float64
。
我对Infer 02
使用了相同的推理:根据声明来推断float字面量1.9
的类型。 但是,除非将浮点文字立即隐式转换为字符串,否则无法将其添加到字符串。 因此,我不确定变量b
的类型。 因此,应该提出一个错误。
现在,我对错误消息感到困惑。
为什么编译器尝试将字符串文字隐式转换为float64
类型?
在一般意义上:当两个操作数都是文字时,编译器如何推断类型? 有什么好的资源可以帮助我更好地理解Go的类型推断系统?
它不会尝试将其转换为float64
类型。 它只是警告字符串文字不是float64
。
它可能说字符串不是浮点数,或者说float不是字符串,但是第二行涵盖了两种情况,即消息mismatched types string and float64
。
Go中的文字和常量没有Go类型。 例如:
1.1234
视为float常数(而不是float32
或float64
) 2
作为int常数 "hi"
作为字符串常量。 当您尝试将常量放入变量中时,Go会根据变量类型尝试将常量适合变量:
type DayCount int
const i = 123456
var a int32 = i
var b uint64 = i
var c int = i
var d int8 = i // compile error: constant 123456 overflows int8
var e DayCount = i
var f DayCount = c // compile error: cannot use c (type int) as type DayCount in assignment
如果变量本身没有类型,而赋值的右侧是一个常量,则编译器将假定该常量的“默认类型”。 对于int常量,这是int
类型;对于float常量,这是float64
。
var a = 1234 // a type will be int
var b = 1.1234567890 // b type will be float64
var c = "gogogo" // c type will be string
您还可以对常量进行一些操作(在将它们转换为普通的go变量之前),该操作在编译时完成:
var a int8 = 25 * 87 // compile error: constant 2175 overflows int8
由于25和87都是常量,因此编译器将在编译程序时将它们倍增,并尝试将其放入失败的变量a
中。 要点是,在运行程序时未完成乘法。
如果尝试混合使用普通变量和常量,则编译器将尝试将常量转换为变量类型:
var a uint64 = 87
var b = 25 * a
// Is the same as
var b = uint64(25) * a
// so b type is uint64
现在关于您问题中的第一种情况
med = (numbers[n/2] + numbers[(n-1)/2]) / 2 // Infer 01
是相同的
med = (numbers[n/int(2)] + numbers[(n-int(1))/int(2)]) / float64(2)
编译器将尝试将常量转换为附近的变量类型。 第一和第二2
正在与int
的除法运算中使用,并转换为int
。 第三个在与float64
的除法中使用,并转换为float64
。 我认为“类型推断”不是这里的合适名称。
在第二种情况下,两个+
运算符都是常量,编译器使用启发式方法,并假设如果其中一个常量是float,则两个常量都应适合float64
(这是有问题的),然后尝试将失败的字符串常量放入float64
中(因此,无法转换为float64
错误)。
第二种情况的行为未在规范中记录,我认为这根本不重要。 IMO有效的编译器可能会尝试将1.9
装入字符串变量并显示另一个错误。
另请参见有关常量的博客,有关常量的 规范 , 有关常量表达式的规范 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.