2013-11-23 wcdj
0 摘要
本文通过对A Tour of Go的实践,总结Go语言的基础用法。
1 Go语言”奇怪用法“有哪些?
1,go的变量声明顺序是:”先写变量名,再写类型名“,此与C/C++的语法孰优孰劣,可见下文解释:
http://blog.golang.org/gos-declaration-Syntax
2,go是通过package来组织的(与python类似),只有package名为main的包可以包含main函数,一个可执行程序有且仅有一个main包,通过import关键字来导入其他非main包。
3,可见性规则。go语言中,使用大小写来决定该常量、变量、类型、接口、结构或函数是否可以被外部包含调用。根据约定,函数名首字母小写即为private,函数名首字母大写即为public。
4,go内置关键字(25个均为小写)。
5,函数不用先声明,即可使用。
6,在函数内部可以通过 := 隐士定义变量。(函数外必须显示使用var定义变量)
7,go程序使用UTF-8编码的纯Unicode文本编写。
8,使用big.Int的陷阱:
http://stackoverflow.com/questions/11270547/go-big-int-factorial-with-recursion
9,从技术层面讲,go语言的语句是以分号分隔的,但这些是由编译器自动添加的,不用手动输入,除非需要在同一行中写入多个语句。没有分号及只需少量的逗号和圆括号,使得go语言的程序更容易阅读。
10,go语言只有一个循环结构——for循环。
11,go里的自增运算符只有——“后++”
12,go语言中的slice用法类似python中数组,关于slice的详细用法可见:http://blog.golang.org/go-slices-usage-and-internals
13,函数也是一个值,使用匿名函数返回一个值。
14,函数闭包的使用,闭包是一个匿名函数值,会引用到其外部的变量。
2 代码实践
/* gerryyang * 2013-11-23 */ package main import ( "fmt" "math" "math/big" "math/cmplx" "math/rand" "net/http" "os" "runtime" "time" ) // Outside a function,every construct begins with a keyword (var,func,and so on) and the := construct is not available // The var statement declares a list of variables; as in function argument lists,the type is last var x,y,z int var c,python,java bool // A var declaration can include initializers,one per variable var x1,y1,z1 int = 1,2,3 // If an initializer is present,the type can be omitted; the variable will take the type of the initializer var c1,python1,java1 = true,false,"no!" // basic types // bool // string // int int8 int16 int32 int64 // uint uint8 uint16 uint32 uint64 uintptr // byte (alias for uint8) // rune (alias for int32,represents a Unicode code point) // float32 float64 // complex64 complex128 var ( ToBe bool = false MaxInt uint64 = 1<<64 - 1 complex complex128 = cmplx.Sqrt(-5 + 12i) ) // Constants are declared like variables,but with the const keyword const Pi = 3.14 // Constants can be character,string,boolean,or numeric values const World = "世界" // Numeric Constants const ( Big = 1 << 100 Small = Big >> 99 // 2 ) type Vertex struct { X int Y int } type Vertex2 struct { Lat,Long float64 } var m map[string]Vertex2 // Map literals are like struct literals,but the keys are required var m2 = map[string]Vertex2{ "gerryyang": Vertex2{ 100,200,},"wcdj": Vertex2{ -300,500,} // If the top-level type is just a type name,you can omit it from the elements of the literal var m3 = map[string]Vertex2{ "math": {20,40},"computer": {30,50},} type Vertex3 struct { X,Y float64 } type MyFloat float64 type Abser interface { Abs() float64 } //////////////////////////////////////////////////////// func main() { fmt.Println("Hello Golang,I'm gerryyang") fmt.Println("The time is",time.Now()) // To see a different number,seed the number generator; see rand.Seed fmt.Println("My favorite number is",rand.Intn(7)) fmt.Printf("Now you have %g problesms\n",math.Nextafter(2,3)) // In Go,a name is exported if it begins with a capital letter fmt.Println(math.Pi) // Notice that the type comes after the variable name fmt.Println(add(42,13)) fmt.Println(add2(42,13)) // multiple results a,b := swap("gerry","yang") fmt.Println(a,b) // named return fmt.Println(split(17)) fmt.Println(split2(17)) // var used fmt.Println(x,z,c,java) fmt.Println(x1,z1,c1,java1) // Inside a function,the := short assignment statement can be used in place of a var declaration with implicit type var x2,y2,z2 int = 1,3 c2,python2,java2 := true,"yes!" fmt.Println(x2,z2,c2,java2) // basic types const f = "%T(%v)\n" fmt.Printf(f,ToBe,ToBe) fmt.Printf(f,MaxInt,MaxInt) fmt.Printf(f,complex,complex) // Constants cannot be declared using the := Syntax const World2 = "和平" const Truth = true fmt.Println(Pi) fmt.Println("你好",World) fmt.Println("世界",World2) fmt.Println("Go rules?",Truth) // Numeric Constants fmt.Println(needInt(Small)) ////fmt.Println(needInt(Big))// error,constant 1267650600228229401496703205376 overflows int ////fmt.Println(needInt64(Big)) // error,same as above ////fmt.Println(needBigInt(big.NewInt(Big)))// error,使用big.Int貌似入参最大类型只支持int64 fmt.Println(needFloat(Small)) fmt.Println(needFloat(Big)) // Go has only one looping construct,the for loop // The basic for loop looks as it does in C or Java,except that the ( ) are gone (they are not even optional) and the { } are required sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) // As in C or Java,you can leave the pre and post statements empty // At that point you can drop the semicolons: C's while is spelled for in Go sum1 := 1 for sum1 < 1000 { sum1 += sum1 } fmt.Println(sum1) // If you omit the loop condition it loops forever,so an infinite loop is compactly expressed ivar := 1 for { if ivar++; ivar > 1000 { fmt.Println("leave out an infinite loop") break } } // The if statement looks as it does in C or Java,except that the ( ) are gone and the { } are required fmt.Println(sqrt(2),sqrt(-4)) // Like for,the if statement can start with a short statement to execute before the condition fmt.Println(pow(3,10),pow(3,3,20)) // If and else fmt.Println(pow2(3,pow2(3,20)) //////////////////////////////////////////////////////////// // A struct is a collection of fields fmt.Println(Vertex{1,2}) // Struct fields are accessed using a dot v := Vertex{1,2} v.X = 4 fmt.Println(v) // Go has pointers,but no pointer arithmetic // Struct fields can be accessed through a struct pointer. The indirection through the pointer is transparent p := Vertex{1,2} q := &p q.X = 1e9 fmt.Println(p) // struct literals // A struct literal denotes a newly allocated struct value by listing the values of its fields p = Vertex{1,2} // has type Vertex q = &Vertex{1,2} // has type * Vertex r := Vertex{X: 1} // Y:0 is implicit s := Vertex{} // X:0 and Y:0 fmt.Println(p,q,r,s) // The expression new(T) allocates a zeroed T value and returns a pointer to it // var t *T = new(T) or t := new(T) pv := new(Vertex) fmt.Println(pv) pv.X,pv.Y = 11,9 fmt.Println(pv) // A slice points to an array of values and also includes a length // []T is a slice with elements of type T as := []int{2,5,7,11,13} fmt.Println("as ==",as) for i := 0; i < len(as); i++ { fmt.Printf("as[%d] == %d\n",i,as[i]) } // Slices can be re-sliced,creating a new slice value that points to the same array // The expression: s[lo:hi] // evaluates to a slice of the elements from lo through hi-1,inclusive fmt.Println("as[1:4] ==",as[1:4]) // missing low index implies 0 fmt.Println("as[:3] ==",as[:3]) // missing high index implies len(s) fmt.Println("as[4:] ==",as[4:]) // Slices are created with the make function. It works by allocating a zeroed array and returning a slice that refers to that array // a := make([]int,5),note,len(a) = 5 // To specify a capacity,pass a third argument to make // b := make([]int,len(b) = 0,cap(b) = 5 // b = b[:cap(b)],len(b) = 5,cap(b) = 5 // b = b[1:],len(b) = 4,cap(b) = 4 s1 := make([]int,5) printSlice("s1",s1) s2 := make([]int,5) printSlice("s2",s2) s3 := s2[:2] printSlice("s3",s3) s4 := s3[2:5] printSlice("s4",s4) // The zero value of a slice is nil // A nil slice has a length and capacity of 0 var s5 []int fmt.Println(s5,len(s5),cap(s5)) if s5 == nil { fmt.Println("slice is nil") } // The range form of the for loop iterates over a slice or map var s6 = []int{1,4,8,16,32,64,128,256,512,1024} for i,v := range s6 { fmt.Printf("2**%d = %d\n",v) } // If you only want the index,drop the ",value" entirely for i := range s6 { s6[i] = 1 << uint(i) } // You can skip the index or value by assigning to _ for _,value := range s6 { fmt.Printf("%d\n",value) } // A map maps keys to values // Maps must be created with make (not new) before use; the nil map is empty and cannot be assigned to m = make(map[string]Vertex2) m["Bell Labs"] = Vertex2{ 40.68433,-74.39967,} fmt.Println(m["Bell Labs"]) // Map literals are like struct literals,but the keys are required fmt.Println(m2) // If the top-level type is just a type name,you can omit it from the elements of the literal fmt.Println(m3) // map // insert,update,retrieve,delete,test m4 := make(map[string]int) m4["date"] = 20131129 fmt.Println("The value:",m4["date"]) m4["date"] = m4["date"] + 1 fmt.Println("The value:",m4["date"]) date,ok := m4["date"] fmt.Println("The value:",date,"Present?",ok) delete(m4,"date") fmt.Println("The value:",m4["date"]) date2,date2,ok) // Function values // Functions are values too hypot := func(x,y float64) float64 { return math.Sqrt(x*x + y*y) } fmt.Println(hypot(3,4)) // Function closures // For example,the adder function returns a closure. Each closure is bound to its own sum variable pos,neg := adder(),adder() for i := 0; i < 10; i++ { fmt.Println(pos(i),neg(-2*i)) } // fibonacci fib := fibonacci() for i := 0; i < 10; i++ { fmt.Println(fib()) } // switch // A case body breaks automatically,unless it ends with a fallthrough statement switch os := runtime.GOOS; os { case "darwin": fmt.Println("OS X") case "linux": fmt.Println("Linux") default: // freebsd,openbsd // plan9,windows... fmt.Printf("%s",os) } // Switch cases evaluate cases from top to bottom,stopping when a case succeeds // Note: Time in the Go playground always appears to start at 2009-11-10 23:00:00 UTC fmt.Println("When's Saturday?") today := time.Now().Weekday() switch time.Saturday { case today + 0: fmt.Println("Today") case today + 1: fmt.Println("Tomorrow") case today + 2: fmt.Println("In two days") case today + 3: fmt.Println("In three days") default: fmt.Println("Too far away") } // Switch without a condition is the same as switch true // This construct can be a clean way to write long if-then-else chains t_now := time.Now() switch { case t_now.Hour() < 12: fmt.Println("Good morning!") case t_now.Hour() < 17: fmt.Println("Good afternoon") default: fmt.Println("Good evening") } // Go does not have classes. However,you can define methods on struct types v3 := &Vertex3{3,4} fmt.Println(v3.Abs()) // In fact,you can define a method on any type you define in your package,not just structs // You cannot define a method on a type from another package,or on a basic type f1 := MyFloat(-math.Sqrt2) fmt.Println(f1.Abs()) // Methods with pointer receivers // Methods can be associated with a named type or a pointer to a named type // We just saw two Abs methods. One on the *Vertex pointer type and the other on the MyFloat value type // There are two reasons to use a pointer receiver. First,to avoid copying the value on each method call (more efficient if the value type is a large struct). Second,so that the method can modify the value that its receiver points to v3 = &Vertex3{3,4} v3.Scale(5) fmt.Println(v3,v3.Abs()) // An interface type is defined by a set of methods // A value of interface type can hold any value that implements those methods var a_interface Abser v4 := Vertex3{3,4} a_interface = f1 // a MyFloat implements Abser a_interface = &v4 // a *Vertex3 implements Abser //a_interface = v4 // a Vertex3,does Not,error! fmt.Println(a_interface.Abs()) // Interfaces are satisfied implicitly var w Writer // os.Stdout implements Writer w = os.Stdout fmt.Fprintf(w,"hello,writer\n") // An error is anything that can describe itself as an error string. The idea is captured by the predefined,built-in interface type,error,with its single method,Error,returning a string: type error interface { Error() string } if err := run(); err != nil { fmt.Println(err) } // Web servers //var h Hello //http.ListenAndServe("localhost:4000",h) } ///////////////////////////////////////////// // func can be used before declare func add(x int,y int) int { return x + y } // When two or more consecutive named function parameters share a type,you can omit the type from all but the last func add2(x,y int) int { return x + y } // multiple results,a function can return any number of results func swap(x,y string) (string,string) { return y,x } // In Go,functions can return multiple "result parameters",not just a single value. They can be named and act just like variables func split(sum int) (x,y int) { x = sum * 4 / 9 y = sum - x return y,not just a single value. They can be named and act just like variables func split2(sum int) (x,y int) { x = sum * 4 / 9 y = sum - x // If the result parameters are named,a return statement without arguments returns the current values of the results return } func needInt(x int) int { return x*10 + 1 } func needInt64(x int64) int64 { return x*10 + 1 } func needBigInt(x *big.Int) (result *big.Int) { result = new(big.Int) result.Set(x) result.Mul(result,big.NewInt(10)) return } func needFloat(x float64) float64 { return x * 0.1 } func sqrt(x float64) string { if x < 0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) } // Variables declared by the statement are only in scope until the end of the if func pow(x,n,lim float64) float64 { if v := math.Pow(x,n); v < lim { return v } return lim } // Variables declared inside an if short statement are also available inside any of the else blocks func pow2(x,n); v < lim { return v } else { fmt.Printf("%g >= %g\n",v,lim) } // can't use v here,though return lim } func printSlice(s string,x []int) { fmt.Printf("%s len = %d cap = %d %v\n",s,len(x),cap(x),x) } // Go functions may be closures. A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } // fibonacci is a function that returns // a function that returns an int. func fibonacci() func() int { p := 0 q := 1 s := 0 return func() int { s = p + q p = q q = s return s } } // The method receiver appears in its own argument list between the func keyword and the method name func (v *Vertex3) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func (f MyFloat) Abs() float64 { if f < 0 { fmt.Println("f < 0 here") return float64(-f) } return float64(f) } // Methods with pointer receivers func (v *Vertex3) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } // Interfaces are satisfied implicitly type Reader interface { Read(b []byte) (n int,err error) } type Writer interface { Write(b []byte) (n int,err error) } type ReadWriter interface { Reader Writer } // error control type MyError struct { When time.Time What string } func (e *MyError) Error() string { return fmt.Sprintf("at %v,%s",e.When,e.What) } func run() error { return &MyError{ time.Now(),"it didn't work",} } // Web servers type Hello struct{} func (h Hello) ServeHTTP( w http.ResponseWriter,r *http.Request) { fmt.Fprint(w,"gerryyang") } /* output: Hello Golang,I'm gerryyang The time is 2013-12-04 22:52:01.336562598 +0800 HKT My favorite number is 6 Now you have 2.0000000000000004 problesms 3.141592653589793 55 55 yang gerry 10 7 7 10 0 0 0 false false false 1 2 3 true false no! 1 2 3 true false yes! bool(false) uint64(18446744073709551615) complex128((2+3i)) 3.14 你好 世界 世界 和平 Go rules? true 21 0.2 1.2676506002282295e+29 45 1024 leave out an infinite loop 1.4142135623730951 2i 9 20 27 >= 20 9 20 {1 2} {4 2} {1000000000 2} {1 2} &{1 2} {1 0} {0 0} &{0 0} &{11 9} as == [2 3 5 7 11 13] as[0] == 2 as[1] == 3 as[2] == 5 as[3] == 7 as[4] == 11 as[5] == 13 as[1:4] == [3 5 7] as[:3] == [2 3 5] as[4:] == [11 13] s1 len = 5 cap = 5 [0 0 0 0 0] s2 len = 0 cap = 5 [] s3 len = 2 cap = 5 [0 0] s4 len = 3 cap = 3 [0 0 0] [] 0 0 slice is nil 2**0 = 1 2**1 = 2 2**2 = 4 2**3 = 8 2**4 = 16 2**5 = 32 2**6 = 64 2**7 = 128 2**8 = 256 2**9 = 512 2**10 = 1024 1 2 4 8 16 32 64 128 256 512 1024 {40.68433 -74.39967} map[gerryyang:{100 200} wcdj:{-300 500}] map[math:{20 40} computer:{30 50}] The value: 20131129 The value: 20131130 The value: 20131130 Present? true The value: 0 The value: 0 Present? false 5 0 0 1 -2 3 -6 6 -12 10 -20 15 -30 21 -42 28 -56 36 -72 45 -90 1 2 3 5 8 13 21 34 55 89 OS X When's Saturday? In three days Good evening 5 f < 0 here 1.4142135623730951 &{15 20} 25 5 hello,writer at 2013-12-04 22:52:01.337206342 +0800 HKT,it didn't work */