表达式(Expressions) 与运算符(Operator)

表达式

表达式是用运算符和函数的描述的一个计算过程。

常量表达式(Constant expressions)

常量表达式在编译时执行,常量表达式中只能使用常量。

使用常量表达式时,需要特别注意未明确声明类型的常量的类型。

const a = 2 + 3.0             // a == 5.0   (untyped floating-point constant)
const b = 15 / 4              // b == 3     (untyped integer constant)
const c = 15 / 4.0            // c == 3.75  (untyped floating-point constant)
const Θ float64 = 3/2         // Θ == 1.0   (type float64, 3/2 is integer division)
const Π float64 = 3/2.        // Π == 1.5   (type float64, 3/2. is float division)
const d = 1 << 3.0            // d == 8     (untyped integer constant)
const e = 1.0 << 3            // e == 8     (untyped integer constant)
const f = int32(1) << 33      // illegal    (constant 8589934592 overflows int32)
const g = float64(2) >> 1     // illegal    (float64(2) is a typed floating-point constant)
const h = "foo" > "bar"       // h == true  (untyped boolean constant)
const j = true                // j == true  (untyped boolean constant)
const k = 'w' + 1             // k == 'x'   (untyped rune constant)
const l = "hi"                // l == "hi"  (untyped string constant)
const m = string(k)           // m == "x"   (type string)
const Σ = 1 - 0.707i          //            (untyped complex constant)
const Δ = Σ + 2.0e-4          //            (untyped complex constant)
const Φ = iota*1i - 1/1i      //            (untyped complex constant)

complex是内置的函数,返回常量:

const ic = complex(0, c)      // ic == 3.75i  (untyped complex constant)
const iΘ = complex(0, Θ)      // iΘ == 1i     (type complex128)

如果常量的值超过了能够表达的范围,这个常量可以作为中间值使用:

const Huge = 1 << 100         // Huge == 1267650600228229401496703205376  (untyped integer constant)
const Four int8 = Huge >> 98  // Four == 4                                (type int8)

除数不能为0:

const n = 3.14 / 0.0          // illegal: division by zero

不可以将常量转换为不匹配的类型:

uint(-1)     // -1 cannot be represented as a uint
int(3.14)    // 3.14 cannot be represented as an int
int64(Huge)  // 1267650600228229401496703205376 cannot be represented as an int64
Four * 300   // operand 300 cannot be represented as an int8 (type of Four)
Four * 100   // product 400 cannot be represented as an int8 (type of Four)

选择表达式(Selector)

选择表达式的格式如下:

x.f

其中f是选择器(selector),类型为f,它不能是空白标记符_

如果x是包名,那么选择的是包中的标记符。

f可以是x的成员、方法、匿名成员、匿名成员的方法,到达f时经过的选择次数是f的深度(depth)。

如果f是x的直接成员,深度为0,f是x的直接匿名成员的成员,深度为f在匿名成员中的深度+1。

选择表达式遵循下面的规则:

x的类型为T或者*T,并且T不是指针和接口,x.f是T中深度最小的名为f的成员。
x的类型为T,T是接口, x.f是x的动态类型的名为f的方法。
如果x是指针,x.f是(*x).f的简写,两者等同

如果按照上面的规则,找不到f,编译或运行时报错。

对于下面的代码:

type T0 struct {
	x int
}

func (*T0) M0()

type T1 struct {
	y int
}

func (T1) M1()

type T2 struct {
	z int
	T1
	*T0
}

func (*T2) M2()

type Q *T2

var t T2     // with t.T0 != nil
var p *T2    // with p != nil and (*p).T0 != nil
var q Q = p

可以有这么些选择方法:

t.z          // t.z
t.y          // t.T1.y
t.x          // (*t.T0).x

p.z          // (*p).z
p.y          // (*p).T1.y
p.x          // (*(*p).T0).x

q.x          // (*(*q).T0).x        (*q).x is a valid field selector

p.M0()       // ((*p).T0).M0()      M0 expects *T0 receiver
p.M1()       // ((*p).T1).M1()      M1 expects T1 receiver
p.M2()       // p.M2()              M2 expects *T2 receiver
t.M2()       // (&t).M2()           M2 expects *T2 receiver, see section on Calls

注意q没有选择M0(),因为M0()的Reciver类型是*T1,类型Q中不能继承T1的方法。

方法表达式(Method expressions)

方法(method)表达式就是方法的实现语句。

MethodExpr    = ReceiverType "." MethodName .
ReceiverType  = TypeName | "(" "*" TypeName ")" | "(" ReceiverType ")" .

与函数的不同的是,方法是有接收者(Receiver)的,如下:

type T struct {
        a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver

var t T

方法是属于类型的,类型的方法和类型的指针的方法是不同的。

类型的方法是一个将接收者作为参数传入的函数,例如在上面例子中:

T.Mv 的类型为 func(tv T, a int) int
T.Mp 的类型为 func(tv *T, a int) int

类型的方法可以直接通过类型名调用:

T.Mv(t, 7)             //注意要传入接收者
(T).Mv(t, 7)
(*T).Mp(&t, 7)         //注意传入的是接收者是指针

类型不能调用类型指针的方法,类型指针可以调用类型的方法:

T.Mp(&t,7)       //Mp是(*T)的方法,不允许T调用
(*T).Mv(t,7)     //Mv是T的方法,*T可以调用

也可以把方法赋值给变量,然后通过变量调用:

f1 := T.Mv; f1(t, 7)         //要传入接受者t
f2 := (T).Mv; f2(t, 7)       //要传入接受者t

f3 := T.Mp; f3(&t, 7)         //要传入接受者&t
f4 := (T).Mp; f4(&t, 7)       //要传入接受者&t

也可以通过该类型的变量调用,这时候不需要传入接收者。

t.Mv(7)
t.Mp(7)

因为变量的方法和类型的方法是不同的,所以不需要传入接收者。

t.Mv 的类型为 func(a int) int
t.Mp 的类型为 func(a int) int

无论一个变量(t)是不是指针(类型为*T的变量),它都既可以调用类型(T)的方法,也可以调用类型指针(*T)的方法。go语言自身代为完成了取址和取值操作。

变量的方法也可以存放单独的变量中,然后通过变量调用:

f := t.Mv; f(7)   // like t.Mv(7)
f := pt.Mp; f(7)  // like pt.Mp(7)
f := pt.Mv; f(7)  // like (*pt).Mv(7)
f := t.Mp; f(7)   // like (&t).Mp(7)
f := makeT().Mp   // invalid: result of makeT() is not addressable

变量的类型为接口时,用同样的方式调用方法:

var i interface { M(int) } = myVal
f := i.M; f(7)  // like i.M(7)

索引表达式(Index expressions)

索引表达式格式如下:

a[x]

a的类型不同,表达式的运行结果不同。

如果a不是字典,x的必须是整数,并且0<= x <len(a)
如果a是数组,返回数组中x位置处的成员,如果x超出数组范围,程序panic
如果a是指向数组的指针,a[x]等同于(*a)[x]
如果a是分片(Slice), a[x]返回x位置处的数组成员,如果x超出范围,程序panic
如果a是字符串,返回x位置处的字符,如果x超出范围,程序panic,且a[x]不能被赋值
如果a是字典(map),x的类型必须是字典的key的类型,返回字典中x对应的值,和表示对应成员是否存在的布尔类型的值(bool)
如果a是字典(map),且a的值是nil,a[x]返回字典中成员类型的零值

分片表达式(Slice expressions)

分片表达式适用于字符串、数组、指向数组的指针和分片。

a[low : high]

返回一个从零开始,长度为high-low的分片。

a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]

得到分片s的情况如下:

s[0] == 2
s[1] == 3
s[2] == 4

分片表达式中low和high省略:

a[2:]  // same as a[2 : len(a)]
a[:3]  // same as a[0 : 3]
a[:]   // same as a[0 : len(a)]

如果a是指向数组的指针,a[low:high]等同于(*a)[low:high]

如果a是字符串、数组、指向数组的指针,low和high的取值范围为:

0 <= low <= high <= len(a)

如果a是分片,low和high的取值范围为:

0 <= low <= high <= cap(a)

low和high超出范围时,引发panic。

如果a是已经声明字符串、分片,返回值也是字符串、分片。

如果a是未声明的字符串,返回一个类型为字符串的变量.

如果a是数组,返回指向这个数组的分片。

完整分片表达式(Full slice expressions)

完整的分片表达式还带有一个max,限定返回的分片的容量为(capacity)为max-low

a[low : high : max]

在完整的分片表达式中,只有low可以省略,默认为0。

如果a是字符串、数组、指向数组的指针,low和high的取值范围为:

0<= low <= high <= max <= len(a)

如果a是分片,low、high和max的取值范围为:

0<= low <= high <= max <= cap(a)

如果超出范围,引发panic。

类型断言表达式(Type assertions expressions)

断言表达式用来判断x是否不为nil,且它的类型是否与T匹配。

x.(T)

如果T不是接口类型,x的类型必须是接口,判断T是否可以成为x的动态类型。

如果T是接口类型,判断x是否实现了接口T。

如果T不是接口类型,x的类型也不是接口,引发panic。

如果断言成立,表达式的值就是类型为T的x,和布尔值true;如果断言不成立,表达式的值是类型T的零值,和布尔值false。

调用表达式(Call expressions)

调用表达式适用于函数和方法:

f(a1, a2, … an)

针对方法使用时,需要带有receiver:

math.Atan2(x, y)  // function call
var pt *Point
pt.Scale(3.5)     // method call with receiver pt

传入值按值、按顺序传递给函数或方法的参数,返回值也是按值传递的。

如果一个函数的返回值,满足另一个参数的传入参数要求,可以写成f(g(parameters_of_g)),例如:

func Split(s string, pos int) (string, string) {
        return s[0:pos], s[pos:]
}

func Join(s, t string) string {
        return s + t
}

if Join(Split(value, len(value)/2)) != value {
        log.Panic("test fails")
}

调用表达式支持变长参数,变长参数必须是最后一个,且类型前是...

例如在下面的函数中:

func Greeting(prefix string, who ...string)

如果以这种方式调用,参数who的值是nil:

Greeting("nobody")

如果以这种方式调用,参数who的值的类型是[]string:

Greeting("hello:", "Joe", "Anna", "Eileen")

如果以这种方式调用,参数who等于s:

s:= []string{"James", "Jasmine"}
Greeting("goodbye:", s...)

运算符(Operator)

运算符用于构成表达式。

Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .

binary_op  = "||" | "&&" | rel_op | add_op | mul_op .
rel_op     = "==" | "!=" | "<" | "<=" | ">" | ">=" .
add_op     = "+" | "-" | "|" | "^" .
mul_op     = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .

unary_op   = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .

运算符都是go语言内置的。

Precedence    Operator
    5             *  /  %  <<  >>  &  &^
    4             +  -  |  ^
    3             ==  !=  <  <=  >  >=
    2             &&
    1             ||

优先级相同的二元运算符按照先左后右的顺序结合:

x / y * z

等同于:

(x / y) * z

算数运算符(Arithmetic operators)

+    sum                    integers, floats, complex values, strings
-    difference             integers, floats, complex values
*    product                integers, floats, complex values
/    quotient               integers, floats, complex values
%    remainder              integers

&    bitwise AND            integers
|    bitwise OR             integers
^    bitwise XOR            integers
&^   bit clear (AND NOT)    integers

<<   left shift             integer << unsigned integer
>>   right shift            integer >> unsigned integer

字符串拼接(String concatenation)

字符串可以用运算符"+"进行拼接:

:= "hi" + string(c)
s += " and good bye"

比较运算符(Comparison operators)

==    equal
!=    not equal
<     less
<=    less or equal
>     greater
>=    greater or equal

逻辑运算符(Logical operators)

&&    conditional AND    p && q  is  "if p then q else false"
||    conditional OR     p || q  is  "if p then true else q"
!     NOT                !p      is  "not p"

地址运算符(Address operators)

&     
*  

读取运算符(Receive operator)

v1 := <-ch
v2 = <-ch
f(<-ch)
<-strobe  // wait until clock pulse and discard received value

类型转换(Conversions)

Conversion = Type "(" Expression [ "," ] ")" .

参考

  1. go Lexical elements