Skip to content

Latest commit

 

History

History
152 lines (91 loc) · 9.09 KB

01 入门.md

File metadata and controls

152 lines (91 loc) · 9.09 KB

入门

Hello World

main包比较特殊。它定义了一个独立可执行的程序,而不是一个库。在main里的main 函数 也很特殊,它是整个程序执行时的入口。

Go语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。实际上,编译器会主动把特定符号后的换行符转换为分号,因此换行符添加的位置会影响Go代码的正确解析。

命令行参数

os包以跨平台的方式,提供了一些与操作系统交互的函数和变量。程序的命令行参数可从os包的Args变量获取;os包外部使用os.Args访问该变量。

os.Args的第一个元素:os.Args[0],是命令本身的名字;其它的元素则是程序启动时传给它的参数。

变量会在声明时直接初始化。如果变量没有显式初始化,则被隐式地赋予其类型的零值(zero value)。

Go语言只有for循环这一种循环语句。for循环有多种形式,其中一种如下所示:

for initialization; condition; post {
    // zero or more statements
}

for循环三个部分不需括号包围。大括号强制要求,左大括号必须和post语句在同一行。

initialization语句是可选的,在循环开始前执行。initalization如果存在,必须是一条简单语句(simple statement),即,短变量声明、自增语句、赋值语句或函数调用。condition是一个布尔表达式(boolean expression),其值在每次循环迭代开始时计算。如果为true则执行循环体语句。post语句在循环体执行结束后执行,之后再次对condition求值。condition值为false时,循环结束。

for循环的这三个部分每个都可以省略,如果省略initializationpost,分号也可以省略:

// a traditional "while" loop
for condition {
    // ...
}

如果连condition也省略了,像下面这样:

// a traditional infinite loop
for {
    // ...
}

这就变成一个无限循环,尽管如此,还可以用其他方式终止循环,如一条breakreturn语句。

for循环的另一种形式,在某种数据类型的区间(range)上遍历,如字符串或切片。

如果连接涉及的数据量很大,这种方式代价高昂。一种简单且高效的解决方案是使用strings包的Join函数。

查找重复的行

map存储了键/值(key/value)的集合,对集合元素,提供常数时间的存、取或测试操作。键可以是任意类型,只要其值能用==运算符比较,值则可以是任意类型。

map中不含某个键时不用担心,首次读到新行时,等号右边的表达式counts[line]的值将被计算为其类型的零值,对于int即0。

map的迭代顺序并不确定,从实践来看,该顺序随机,每次运行都会变化。这种设计是有意为之的,因为能防止程序依赖特定遍历顺序,而这是无法保证的。

bufio包,它使处理输入和输出方便又高效。Scanner类型是该包最有用的特性之一,它读取输入并将其拆成行或单词;通常是处理行形式的输入最简单的方法。

程序使用短变量声明创建bufio.Scanner类型的变量input

input := bufio.NewScanner(os.Stdin)

该变量从程序的标准输入中读取内容。每次调用input.Scan(),即读入下一行,并移除行末的换行符;读取的内容可以调用input.Text()得到。Scan函数在读到一行时返回true,不再有输入时返回false

fmt.Printf函数对一些表达式产生格式化输出。该函数的首个参数是个格式字符串,指定后续参数被如何格式化。各个参数的格式取决于“转换字符”(conversion character),形式为百分号后跟一个字母。举个例子,%d表示以十进制形式打印一个整型操作数,而%s则表示把字符串型操作数的值展开。

Printf有一大堆这种转换,Go程序员称之为动词(verb)。下面的表格虽然远不是完整的规范,但展示了可用的很多特性:

%d          十进制整数
%x, %o, %b  十六进制八进制二进制整数%f, %g, %e  浮点数3.141593 3.141592653589793 3.141593e+00
%t          布尔true或false
%c          字符rune) (Unicode码点)
%s          字符串
%q          带双引号的字符串"abc"或带单引号的字符'c'
%v          变量的自然形式natural format%T          变量的类型
%%          字面上的百分号标志无操作数

默认情况下,Printf不会换行。按照惯例,以字母f结尾的格式化函数,如log.Printffmt.Errorf,都采用fmt.Printf的格式化准则。而以ln结尾的格式化函数,则遵循Println的方式,以跟%v差不多的方式格式化参数,并在最后添加一个换行符。(译注:后缀fformatlnline。)

os.Open函数返回两个值。第一个值是被打开的文件(*os.File),其后被Scanner读取。os.Open返回的第二个值是内置error类型的值。如果err等于内置值nil,那么文件被成功打开。如果err的值不是nil,说明打开文件时出错了。

函数和包级别的变量(package-level entities)可以任意顺序声明,并不影响其被调用。

map是一个由make函数创建的数据结构的引用。map作为参数传递给某函数时,该函数接收这个引用的一份拷贝(copy,或译为副本),被调用函数对map底层数据结构的任何修改,调用者函数都可以通过持有的map引用看到。

ReadFile函数返回一个字节切片(byte slice),必须把它转换为string,才能用strings.Split分割。

实现上,bufio.Scannerioutil.ReadFileioutil.WriteFile都使用*os.FileReadWrite方法,但是,大多数程序员很少需要直接调用那些低级(lower-level)函数。

GIF动画

常量声明和变量声明一般都会出现在包级别,所以这些常量在整个包中都是可以共享的,或者你也可以把常量声明定义在函数体内部,那么这种常量就只能在函数体内用。目前常量声明的值必须是一个数字值、字符串或者一个固定的boolean值。

获取URL

Go语言在net这个强大package的帮助下提供了一系列的package,使用这些包可以更简单地用网络收发信息,还可以建立更底层的网络连接,编写服务器程序。

并发获取多个URL

goroutine是一种函数的并发执行方式,而channel是用来在goroutine之间进行参数传递。main函数本身也运行在一个goroutine中,而go function则表示创建一个新的goroutine,并在这个新的goroutine中执行这个函数。

当一个goroutine尝试在一个channel上做send或者receive操作时,这个goroutine会阻塞在调用处,直到另一个goroutine从这个channel里接收或者写入值,这样两个goroutine才会继续执行channel操作之后的逻辑。

本章要点

switch, Go语言并不需要显式地在每一个case后写break,语言默认执行完case后的逻辑语句会自动退出。当然了,如果你想要相邻的几个case都执行同一逻辑的话,需要自己显式地写上一个fallthrough语句来覆盖这种默认行为。

Go语言里的switch还可以不带操作对象(译注:switch不带操作对象时默认用true值代替,然后将每个case的表达式和true值进行比较);可以直接罗列多种条件,像其它语言里面的多个if else一样,下面是一个例子:

func Signum(x int) int {
    switch {
    case x > 0:
        return +1
    default:
        return 0
    case x < 0:
        return -1
    }
}

这种形式叫做无tag switch(tagless switch);这和switch true是等价的。

像for和if控制语句一样,switch也可以紧跟一个简短的变量声明,一个自增表达式、赋值语句,或者一个函数调用。

break和continue语句会改变控制流。和其它语言中的break和continue一样,break会中断当前的循环,并开始执行循环之后的内容,而continue会跳过当前循环,并开始执行下一次循环。这两个语句除了可以控制for循环,还可以用来控制switch和select语句。

continue会跳过内层的循环,如果我们想跳过的是更外层的循环的话,我们可以在相应的位置加上label,这样break和continue就可以根据我们的想法来continue和break任意循环。这看起来甚至有点像goto语句的作用了。

指针:指针是可见的内存地址,&操作符可以返回一个变量的内存地址,并且*操作符可以获取指针指向的变量内容,但是在Go语言里没有指针运算,也就是不能像c语言里可以对指针进行加或减操作。

方法和接口: 方法是和命名类型关联的一类函数。Go语言里比较特殊的是方法可以被关联到任意一种命名类型。接口是一种抽象类型,这种类型可以让我们以同样的方式来处理不同的固有类型,不用关心它们的具体实现,而只需要关注它们提供的方法。