标签:action mod lin 隐藏 text tor 粉丝 数据 可见
赋值(Assignment)
变量的值能够通过赋值操作符 = 来更新, v = 10。
x = 1 // 具名变量x *p = true // 指针变量 person.name = "bob" // 结构体struct的字段 count[x] = count[x] * scale // 数组、切片或者map的某个元素
count[x] *= scale这样的缩略形式能省去不少反复的工作,同一时候数字变量还能通过++递增或者--递减:
v := 1 v++ // same as v = v + 1; v becomes 2 v-- // same as v = v - 1; v becomes 1 again
x, y = y, x a[i], a[j] = a[j], a[i]再比方计算两个数的最大公约数(GCD):
func gcd(x, y int) int { for y != 0 { x, y = y, x%y } return x }或者计算斐波那契数列第N个值:
func fib(n int) int { x, y := 0, 1 for i := 0; i < n; i++ { x, y = y, x+y } return x }
i, j, k = 2, 3, 5
可是假设表达式较为复杂时,应该尽量避免元组赋值,分开赋值的可读性会更好。
一些特定的表达式。比如函数调用有多个返回值,这样的情况下 = 左边必须有相应数量的变量来进行赋值:
f, err = os.Open("foo.txt") // 函数调用有两个返回值
非常多时候,函数会利用这样的多返回值特性来额外返回一个值,这个值会说明函数的调用是否成功(可能是一个error类型变量err,或者bool类型变量ok)。在map中查找某个值,类型断言。从channel中接收值等等都会有两个返回值。当中第二个值就是一个bool类型:
v, ok = m[key] // map lookup v, ok = x.(T) // type assertion v, ok = <-ch // channel receive
Go语言还支持匿名变量 (用过函数式语言的读者应该了解这样的机制),我们能够把不想要的值赋给空白标示符(=左边的变量数目和右边的值数目必须相同):
_, err = io.Copy(dst, src) // 仅仅关心Copy的成功与否,不关心详细Copy的字节数,因此丢弃第一个值 _, ok = x.(T) // 仅仅关心类型断言的成功与否。不关心x的详细值,因此丢弃第一个值
2.可赋值性
上面的赋值语句是一种显式赋值。可是某些情况下,会发生隐式赋值:在函数调用中,隐式赋值给函数參数;函数返回时。隐式赋值给return的操作数;还有相似以下的组合类型:
medals := []string{"gold", "silver", "bronze"}这里就是隐式赋值,等价的显式形式是这样的:
medals[0] = "gold" medals[1] = "silver" medals[2] = "bronze"
相同的还有map、channel类型等,都支持这样的隐式赋值。
不管是用显式赋值或隐式赋值,仅仅要 = 左右两边有相同的类型就可以。
针对不同类型的详细赋值规则会在兴许章节详细解说。对于我们眼下已经讨论过的那些类型,规则是非常easy的:类型必须准确匹配(因此Go是强类型的静态语言)。nil能够被赋值给interface或者其他引用类型。
常量(constant)赋值在类型转换时非常有灵活性。能够避免大多数显式类型转换:
const x = 112 var v float64 = x fmt.Println(v) //output:112两个变量是否能用 == 或 != 比較取决于可赋值性,a == b 仅仅有在a = b可行时才干推断。
类型声明
变量的类型定义了变量的一些个性化属性。比如变量占领的内存大小、在内存中的排列,变量内部的组织形式,变量支持的操作。变量的行为method等。
在实际项目中。非常多自己定义类型都有相同的底层类型。比如:int能够是循环的索引,时间戳。文件描写叙述符或者一个月份。float64类型能够是车辆行驶速度,温度。
使用type就能够声明具名类型,这样就能够在底层类型之上构建自己的须要的时间戳。文件描写叙述符等类型。
type name underlying-type具名类型声明一般都发生在package级别,因此该类型是包内可见的,假设类型是导出的(首字母大写)。那么就是全局可见的。为了解释类型声明,这里把不同的温度计量单位设置为不同的类型:
package tempconv import "fmt" type Celsius float64 type Fahrenheit float64 const ( AbsoluteZeroC Celsius = -273.15 FreezingC Celsius = 0 BoilingC Celsius = 100 ) func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) } func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }上面定义了两个类型Celsius(摄氏度)和Fahrenheit(华氏度),作为温度的两种不同单位。
即使两个类型的底层类型是相同的float64。可是它们是全然不同的类型,所以它们彼此之间不能比較,也不能在算术表达式中结合使用,这样的规则能够避免错误的使用两种不同的温度单位,由于两个温度单位的类型是不同的,CToF和FToC返回的两个值也是全然不同的。
对于每一个类型T,都有一个相应的类型转换T(x),能够把x转换为T类型。
当且仅当两个类型具有相同的底层类型时。才干进行类型转换,比如上文中的Celsius和Fahrenheit。或者两个类型都是指针类型,指向的是同一个底层类型。
数值类型之间、string和[]byte之间都能够进行转换,这些转换可能会改变值的表现形式。
比如。将浮点数转化为整数会截取掉小树部分;将string转化为[]byte切片会分配内存空间创建string的一份拷贝(内存拷贝往往是性能瓶颈之中的一个)。总之类型转换是编译期完毕的。在执行期是不会失败的。
具名类型的底层类型决定了它的结构和表现形式。也决定了它支持的基本操作(能够理解为继承自底层类型),就好像直接使用底层类型一样。因此对于Celsius和Fahrenheit来说,float64支持的算术操作。它们都支持:
fmt.Printf("%g\n", BoilingC - FreezingC) // "100" °C boilingF := CToF(BoilingC) fmt.Printf("%g\n", boilingF - CToF(FreezingC)) // "180" °F fmt.Printf("%g\n", boilingF - FreezingC) // compile error: type mismatch再比方:
type temp int func main() { var x1 temp = 1 var x2 temp = 2 fmt.Println(x1 + x2)//output:3 }
假设两个值有相同的具名类型。那么就能够用比較操作符==和<进行比較;或者两个值,一个是具名类型,一个是具名类型的底层类型。也能够进行比較。可是两个不同的具名类型是不能够直接比較的:
var c Celsius var f Fahrenheit fmt.Println(c == 0) // "true" fmt.Println(f >= 0) // "true" fmt.Println(c == f) // compile error: type mismatch fmt.Println(c == Celsius(f)) // "true"!
我们都知道浮点数是不精确的表达形式,因此两个浮点数之间的比較是须要格外小心的。这里注意最后一个类型转换后浮点数之间的比較。Celsius(f)没有改变f的值,是由于c和f两个都是初始化为0,所以能够放心比較。
假设在项目中有些地方须要反复的去写一个复杂类型时,那么使用具名变量能够带来极大的便利。
我们还能够为具名类型定义特有的行为。这些行为就是Go语言中的类型方法(method),在兴许章节我们还会详细解说。
以下的代码定义了Celsuis类型的一个methond:String,
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
c := FToC(212.0) fmt.Println(c.String()) // "100°C" fmt.Printf("%v\n", c) // "100°C"; no need to call String explicitly fmt.Printf("%s\n", c) // "100°C" fmt.Println(c) // "100°C" fmt.Printf("%g\n", c) // "100"; does not call String fmt.Println(float64(c)) // "100"; does not call String
欢迎大家增加Go语言核心技术QQ群894864,里面热心大神非常多哦。
举报
0条评论