Go 语言学习(1)

稍微记一些要点。

golang 作为 C 语言创始人的作品的一个主要的设计初衷是“减少语言的复杂性”,当初 C++ 的出现不仅仅引入了 OOP、还有 generic programming、exception handling 等等,这一切导致 C++ 被认为是 expert-friendly 的语言。说实在的搞了这么多年的 C++ 程序仍然时常被一些问题困扰的我也比较赞同这个“突破点”。最近两年 C++ 的盛会不少,很大的一部分讨论其实开始集中在“简化”这个问题上,不论是 auto 的引入还是 for each 语法,它们都试图让学习者能够更加简洁的表达自己的想法,消除背后的复杂性。但是从另外一个角度来说,语言的设计更像是“painting”,你可以多画两笔,然而一旦一个语言特性被开启,事后很难消除它导致的影响(不论好还是坏),C++ 的确已经庞大到… 你看过 B 同学最近的 C++ Programming Language 一书的厚度就明白了。这就导致了新语言从头设计的行为。

go 的出现基本抛弃了原先 C 系列语言设计的很多细节,有一些现代语言的感觉。

package

使用 import 引入外部的 package,可以为其 assign 一个 alias,通常用一组 import 的时候用 () 避免重复写 import,这个比 java 和 C++ 好一点。一般说来 go 的 package 按照目录结构组织,同时 go 也提供了命令行工具方便下载一些 go 的 lib。

几个文件可以共用一个 package 名称,每个文件中只有 CamelCase 的才是能被外部访问的。

变量声明与定义

标准写法是 var 加上名称,后面加类型,可以一次声明多个。可以用 = 进行多个变量的赋值,这时因为类型可以从值 infer 出来所以可以省略类型。更简单的是直接用 := 连接变量名和值,连 var 也可以省略掉。类型的转换必须显式表达。

常数

与变量不同的是使用 const 标识,另外不能用 := 这种语法。值得注意的是,常值整数是高精度的,如果不写类型,这个常数可以当成任意可以转换的类型来用。

控制结构

对应的 if/for 都可以省略括号,或者在 { 之前定义一些局部的变量。除了三段式的 for,也可以只将中间的判断语句保留,这相当于 while loop,如果什么都没有对应就是 while (true) 了。类似 switch/case/default 也是类似的改造,只是与 C/C++ 不一样这个不会 fall through。

指针

go 拥有指针,但却没有指针的运算,这意味着指针的作用仅仅在于传递某些复杂的对象。对一个对象取地址仍然使用 &,对应获得的指针类型就是 *Type。通常可以使用 new(Type) 产生 *Type。

数组

go 有内置数组,其格式为 [长度]Type,这个跟指针有点类似,都是把修饰放在类型前面,不过这里的长度不能改变。如果需要访问变长的数组,需要通过 slice,而 slice 的类型为 []Type,这里没有长度,可以使用 len 获得其长度,cap 获得其 capacity,可以使用 []Type{…} 初始化一个 slice,通过 [a:b] 可以将一个 slice 变成其子 slice。通过 make(slice 类型, 容量) 可以构造空 slice,空 slice 为 nil。

遍历 slice 可以用 range,返回的是 index 和值,如果不希望使用其中某个变量可以用 _ 占位。

map

这个类型写出来是 map[KeyType]ValueType,需要使用 make 产生空 map,然后通过 map_var[Key] = Value 为其添加元素。类似 slice 可以直接用 {Key: Value…} 的形式为 literal 初始化。map_var[Key] 可以返回 Value,还可以返回 tuple,第二个元素表示 Key 是否在 map 里面存在。

自定义类型

go 将数据和方法分开,数据存放在 struct 里面,通过 type MyType struct {} 这种结构来定义,从这个角度来说,const/var/func 这些东西的定义都非常的一致,关键字->名字->实际类型描述。

函数定义

go 里面的函数除了参数列表可以把相同的类型参数的类型 collapse 在一起以外,还可以返回多个值(需要括号括起来),返回值可以有名称,这样在函数体里面可以直接使用,在函数名前面加 MyType* 类型作为前缀可以为 MyType 添加方法。

函数本身也可以作为 value,同时可以用来产生 closure。函数的类型是 func(…)(…),因此可以写一个函数返回函数。

函数参数可以用 …type 表示变长参数。

带有前缀的函数也称为方法,注意他们有一定的限制性:不能为基本类型定义,但是可以为 type 定义的基本类型的别名(注:go 里面虽然是别名,但是类型会被区别对待,这跟 C++ 截然不同)定义方法;不能为其他 package 里面的类型定义方法。其实这个说不好是不是个好的限制,像 ruby 那种过分的打开 class 向里面塞各种方法的手段有点过了,不过很多时候的确希望在别人提供的对象上加上一些自己的方法满足自己的一些逻辑。

程序入口

需要把 package 设为 main 另外提供 main 函数。

接口

与 type MyType struct 类似,接口没有数据只有方法为 type MyInterface interface {},如果为某个类型提供了 interface 需要的方法,我们就可以通过赋值将一个类型赋值给对应的 interface,而 interface 可以作为函数的参数传递,可以调用其方法作用在对应的对象上。interface 可以包括其他的 interface。interface 只需要存在复合条件的实现就可以将对应的类型进行转换,不需要关联。

出错处理是一种特殊的 interface,即提供 Error() string 实现即可的类型,因此可以通过返回 error 来表示处理是否有问题,通常可以返回 nil 来表示没有 error。

——————
And God went up from him in the place where he talked with him

Go 语言学习(1)

hello world in go

今天终于编译好了 gcc 4.6,用来体会一下 google go:

package main

import "fmt"

func main() {
fmt.Println("Hello, 世界")
}

编译和 C/C++ 类似

gccgo hello.go -o hello

执行,

$ ./hello
Hello, 世界
$ ldd hello
        libgo.so.0 => /usr/lib64/libgo.so.0 (0x00002b5604023000)
        libm.so.6 => /lib64/libm.so.6 (0x0000003d9ee00000)
        libgcc_s.so.1 => /usr/lib64/libgcc_s.so.1 (0x00002b5604c5e000)
        libc.so.6 => /lib64/libc.so.6 (0x0000003d9ea00000)
        /lib64/ld-linux-x86-64.so.2 (0x0000003d9da00000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003d9f600000)

很有意思的语言,编译非常快,也是产生 native code。

——————
And Enoch walked with God: and he was not; for God took him.

hello world in go