我已经开始学习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
使用了相同的推理:
浮点文字1.9
的类型是根据声明推断出来的。但是,除非将字符串隐式转换为字符串,否则不能将float文本添加到字符串中。所以,我不确定变量b
的类型。因此,应该提出错误。
现在,我对错误消息感到困惑。
为什么编译器会尝试将字符串文字隐式转换为float64
类型?
一般来说:当两个操作数都是文字时,编译器如何推断类型?有哪些好的资源可以帮助我更好地理解Go的类型推理系统?
答案 0 :(得分:2)
它不会尝试将其转换为float64
类型。它只是警告字符串文字不是float64
。
可以说字符串不是浮点数,或浮点数不是字符串,但第二行包含消息mismatched types string and float64
的两种情况。
答案 1 :(得分:1)
Go中的文字和常量没有Go类型。例如:
1.1234
视为浮动常量(而不是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
如果变量本身没有类型且赋值的右侧是常量,则编译器将采用&#34;默认类型&#34;常数。对于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
你也可以对常量做一些操作(在转换为普通变量之前),这是在编译时完成的:
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
。我不认为&#34;类型推断&#34;这是一个合适的名字。
在第二种情况下,两个+
运算符都是常量,编译器使用启发式方法并假设如果其中一个常量是float,则两个常量都应该适合float64
(这是错误的)并且尝试将字符串常量放入失败的float64
中(因此无法转换为float64
错误。)
第二种情况没有记录在规范中,我认为它根本不重要。 IMO有效的编译器可能会尝试将1.9
放入字符串变量并显示另一个错误。
另请参阅blog on constants,spec on constants,spec on constants expressions。