首页 > 技术文章 > <html>

yxysuanfa 2017-08-20 18:29 原文

赋值(Assignment)

变量的值能够通过赋值操作符 = 来更新, v = 10。

x = 1                       // 具名变量x
*p = true                   // 指针变量
person.name = "bob"         // 结构体struct的字段
count[x] = count[x] * scale // 数组、切片或者map的某个元素

算数操作符和位操作符都有相应的一元操作符形式。 v = v + x 等价于 v += x。比如:
count[x] *= scale
这样的缩略形式能省去不少反复的工作,同一时候数字变量还能通过++递增或者--递减:
v := 1
v++    // same as v = v + 1; v becomes 2
v--    // same as v = v - 1; v becomes 1 again

1.元组(tuple) 赋值
元组赋值同意一次性给多个变量赋值。= 右边的表达式会在左边的变量被更新之前先求值,当=左右两边都有相同的变量时,这样的求值方式会非常实用,比如交换两个变量的值:
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) }

非常多自己定义类型都会定义一个String method,由于在调用fmt包的函数去打印该类型时,String会控制该类型的打印方式(这里事实上是实现了Stringer这个接口,和其他OO语言不同,Go语言的接口实现是隐式的):
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,里面热心大神非常多哦。

版权声明:本文为Sunface原创文章。请任意转载并注明出处。同一时候欢迎大家增加Golang隐修会,QQ群894864,大神非常多。

举报

相关文章推荐

对个帖子的总结:严重声明:C语言中不存在数组的类型。它不过一个概念!

http://bbs3.chinaunix.net/viewthread.php?tid=1631299这篇文章讲到了C语言指针的理解。楼主尽管理解了指针,可是说话有点偏激。观点不全然正确。 C语言中不仅不存在数组类型,也不存在char, short, int, long, float, double等等这些类型,非要说C存在一种类型,那也能够说仅仅有一种类型:内存段。何为内存段?就是一块内存。临时称为raw_piece.<b

Go语言核心之美 1.1-命名篇

一.命名篇 1.Go的函数、变量、常量、自己定义类型、包(Package)的命名方式遵循下面规则: 1)首字符能够是随意的Unicode字符或者下划线 2)剩余字符能够是Unicode字符、下划线、...

欢迎关注CSDN程序人生公众号

关注程序猿生活。汇聚开发轶事。

Go语言的复合数据类型struct,array。slice,map

1、结构体 struct 定义格式: type 结构体名称 struct { 属性1 类型 属性2 类型 ... } 结构体的初始化能够使用new关键词和var关键词。不同的是假设使用new,则返回类型是一个指针,使用var,则是结构体自身。 type User struct { name

HIBERNATE - 符合Java习惯的关系数据库持久化

Hibernate參考文档3.1.2文件夹前言 1. 翻译说明 2. 版权声明1. Hibernate入门 1.1. 前言 1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个...
  • 微博
    微信
    QQ
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多仅仅同意输入30个字)

推荐阅读