混淆与Go中文字的类型推断

时间:2017-12-07 14:56:20

标签: go types type-inference

我已经开始学习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的类型推理系统?

2 个答案:

答案 0 :(得分:2)

它不会尝试将其转换为float64类型。它只是警告字符串文字不是float64

可以说字符串不是浮点数,或浮点数不是字符串,但第二行包含消息mismatched types string and float64的两种情况。

答案 1 :(得分:1)

Go中的文字和常量没有Go类型。例如:

  • 1.1234视为浮动常量(而不是float32float64
  • 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 constantsspec on constantsspec on constants expressions