Gopl Chaper 01 要点
-Go是编译型语言,当你运行go run hello.go
,会经历编译,链接,执行。
Go原生支持Unicode编码。
Go通过package的概念组织代码,每个源代码文件属于某个package。
package
main
比较特殊,它定义了单独执行的程序,而不是一个库,因此main包是程序的入口。可以使用
import
的方式引入其它package,但要注意不要少引入或多余引入package,这样程序将不能编译通过。Go不允许未使用的局部变量存在
一个function由关键字
func
,名字,参数列表,返回列表,函数体Go语言中的语句不需要分号在结尾,除非多条语句写在一行
注意
{
要跟func
在同一行变量通过
var
定义:1var s, sep string
变量可以在定义时初始化,也可以不进行初始化,未进行初始化的,如果是整数,默认值0,string默认值是 空串""
这四种声明变量的方式都是等效的:
1234s := ""var s stringvar s = ""var s string = ""但还是有一定区别的:第一种方式只能在一个function内部使用,不是package层的变量。推荐使用前两种方式进行初始化变量,第一种方式,显示初始化变量,意味着初始化值是很重要的,第二种方式,没有显示初始化值,意味着默认初始化不重要。
字符串的加号相当于拼接
i := 1
赋值语句,根据右边赋值的类型决定了变量的类型i++
作为一种声明,而不是表达式。因此j = i++
是不合法的,而且--i
形式也不合法Go语言中for循环永远都是没有括号的
123for initialization ; condition ; post {// zero or more statements}如果没有
initialization
和post
,那么分号可以省略变成如下形式:1234567for condition {// zero or more statements}// a traditional infinite loopfor {// ...}下面代码循环了一个slice
12345678910111213package mainimport ("fmt""os")func main() {s, sep := "", ""for _, arg := range os.Args[1:] {s+=sep + argsep = " "}fmt.Println(s)}在for循环的每次迭代,range都产生两个值,index和value
下面一段代码:
123456789101112131415161718192021package mainimport ("bufio""fmt""os")func main() {counts := make(map[string]int)input := bufio.NewScanner(os.Stdin)for input.Scan() {counts[input.Text()]++}// NOTE: ignoring potential errors from input.Err()for line, n := range counts {if n > 1 {fmt.Printf("%d\t%s\n", n, line)}}}make生成的counts是key为string,value为int的map。input是stdin的输入流。
range counts
中取出的key为line,value为n下面一段代码从文件读取
12345678910111213141516171819202122232425262728293031323334353637package mainimport ("bufio""fmt""os")func main() {counts := make(map[string]int)files := os.Args[1:]if len(files) == 0 {countLines(os.Stdin, counts)} else {for _, arg := range files {f, err := os.Open(arg)if err != nil {fmt.Fprintf(os.Stderr, "dup2: %v\n", err) // %v :any value in a natural for matcontinue}countLines(f, counts)f.Close()}}for line, n := range counts {if n > 1 {fmt.Printf("%d\t%s\n", n, line)}}}func countLines(f *os.File, counts map[string]int) {input := bufio.NewScanner(f)for input.Scan() {counts[input.Text()]++}// NOTE: ignoring potential errors from input.Err()}os.Open
返回两个参数,第一个是*os.File
,第二个是error type
,如果err
为nil
。这段代码中先进行了countLines方法调用,后定义了countLines方法,在go语言中这种前后顺序是无关紧要的。
再看一段利用
ioutils.ReadFile
读文件的代码12345678910111213141516171819202122232425package mainimport ("fmt""io/ioutil""os""strings")func main() {counts := make(map[string]int)for _, filename := range os.Args[1:] {data, err := ioutil.ReadFile(filename)if err != nil {fmt.Fprintf(os.Stderr, "dup3: %v\n", err)continue}for _, line := range strings.Split(string(data), "\n") {counts[line]++}}for line, n := range counts {if n > 1 {fmt.Printf("%d\t%s\n", n, line)}}}下面看跟http相关的代码
12345678910111213141516171819202122232425package mainimport ("fmt""io/ioutil""net/http""os")func main() {for _, url := range os.Args[1:] {resp, err := http.Get(url)if err != nil {fmt.Fprintf(os.Stderr, "fetch: %v\n", err)os.Exit(1)}b, err := ioutil.ReadAll(resp.Body)resp.Body.Close()if err != nil {fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)os.Exit(1)}fmt.Printf("%s", b)}}很好理解,不多解释了
下面看一个跟routine相关的程序
123456789101112131415161718192021222324252627282930313233343536373839package mainimport ("fmt""io""io/ioutil""net/http""os""time")func main() {start := time.Now()ch := make(chan string)for _, url := range os.Args[1:] {go fetch(url, ch) // start a goroutine}for range os.Args[1:] {fmt.Println(<-ch) // receive from channel ch}fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())}func fetch(url string, ch chan<- string) {start := time.Now()resp, err := http.Get(url)if err != nil {ch <- fmt.Sprint(err) // send to channel chreturn}nbytes, err := io.Copy(ioutil.Discard, resp.Body)resp.Body.Close() // don't leak resourcesif err != nil {ch <- fmt.Sprintf("while reading %s: %v", url, err)return}secs := time.Since(start).Seconds()ch <- fmt.Sprintf("%.2fs %7d %s", secs, nbytes, url)}routine和channel在go语言中都是跟多线程并发相关的很重要的概念,会在后边详细介绍