hello,world | |
hello,world 经典示例 | package main import "fmt" func main() { ??? fmt.Println("你好,Go") } |
Go 基础知识 | Go 是编译型的语言 |
Go 的工具链将程序的源文件转变成机器相关的原生二进制指令 | |
这些工具可以通过单一的 go 命令配合其子命令进行使用; 最简单的子命令是 run ,go run 将一个或多个以 " .go " 为后缀的源文件进行编译 、 链接,然后直接运行生成的可执行文件 go run 子命令有点类似 Python 的执行,但实际上是生成可执行文件,但执行后不保留可执行文件 | |
// 执行示例 $ go run hello.go $?你好,Go | |
Go 原生地支持 Unicode,所以 Go 语言可以处理所有国家的语言 | |
使用 go build 命令来将源文件编译成一个可执行文件(二进制程序) 不用进行任何其他处理,随时执行 | |
$ go build -o a.out ./helloworld.go? // -o 指定生成的可执行文件名 $ ./a.out $ 你好,Go | |
Go 代码结构 与 package 声明 | Go 代码是使用包来组织的 包,类似于其他语言中的库和模块 |
一个包由一个或多个 '.go' 源文件组成,放在一个文件夹中 该文件夹的名字描述了包的作用 | |
每一个源文件的开始,都用 package 进行声明 例子中是 package main ,指明了当前文件属于哪个包 | |
package 声明的后面,紧跟着当前文件要导入的其他包的列表 接下来才是存储在文件中的程序声明 | |
Go 标准库 简介 | Go 的标准库中有 100 多个包用来完成输入 、输出 、排序 、文本处理等常规任务; 例如,fmt 包中的函数用来格式化输出和扫描输入; Println 是 fmt 包中一个基本的输出函数,它输出一个或多个用空格分隔的值,结尾使用一个换行符,这样看起来这些值是单行输出 |
main 包 与 main 函数 | 名为 main 的包比较特殊,是用来定义一个独立的可执行程序,而不是库 |
在 main 包中,函数 main 也是特殊的,不管在什么程序中,函数 main 做什么事情,main 函数总是程序开始执行的入口 | |
当然,main 函数通常调用其他包中的函数来做更多的事情,比如 fmt.Println | |
import 声明 导入工具包 | 我们需要告诉编译器,源文件需要哪些包,用 package 声明后面的 import 声明, 来导入这些包 |
必须精确地导入所需要的包 | |
缺失导入包或导入了未使用的包,这两种情况,都会造成编译后失败 | |
这种严格的要求可以防止程序演化中引用不需要的包 | |
import 声明必须跟在 package 声明之后 | |
源文件结构 | import 导入声明后面,是组成程序的 函数(func)、变量(var)、 常量(const)、类型(type)的声明 |
在大部分情况下,上面这些函数 、变量 、常量 、类型的声明的顺序没有特别的要求 | |
函数声明 | 一个函数的声明由 func 关键字 、函数名 、参数列表(main 函数为空)、返回值列表(可以为空)、被包裹在 '{ }' 中的函数体组成,函数体定义了函数的功能 |
不以分号结尾 | Go 不需要在语句或声明后面使用分号结尾; 除非有多个语句或声明出现在同一行,则语句或声明之间用分号隔开(不建议) |
换行解析 | 事实上,跟在特定符号后面的换行符被转换为分号,在什么地方进行换行会影响对 Go 代码的解析 |
例如," { " 符号必须和关键字 func 在同一行,不能单独成行;在 x + y 这个表达式中,换行符可以在 + 操作符的后面,但不能在 + 操作符的前面 | |
Go 对于代码的格式化要求非常严格 | |
gofmt 工具将代码以标准格式重写,go 工具的 fmt 子命令使用 gofmt 工具来格式化指定包里的所有文件或者当前文件夹中的文件(默认情况下) | |
本书中包含的所有 Go 源代码文件都使用 gofmt 运行过,应该养成对自己的代码使用 gofmt 工具的习惯 | |
定制一个标准的格式,可以省去大量无关紧要的辩论;更重要的是,如果允许随心所欲的格式,各种自动化的源代码转换工具将不可用 | |
许多文本编辑器可以配置为每次在保存文件时自动运行 gofmt ,因此源文件总可以保持正确的形式 | |
此外,一个相关的工具 goimports 可以按需管理导入声明的插入和移除; 此工具不是标准发布版的一部分,可以通过执行下面的命令获取到: $ go get golang.org/x/tols/cmd/goimports | |
对大多数用户来说,按照常规方式下载 、编译包,执行自带的测试,查看文档等操作,使用 go 工具都可以实现 |
命令行参数 | |
背景 | 大部分程序处理输入,然后产生输出,这就是关于计算的大致定义; 但是程序怎样获取数据的输入呢? 一些程序自己生成数据,更多的时候,输入来自一个外部源: 文件 、网络连接 、其他程序的输出 、键盘 、命令行参数等 ... |
命令行参数 概览 | os? 包提供了一些函数和变量,以一种 "与平台无关" 的方式和操作系统打交道 |
命令行参数以 os 包中的 Args 名字的变量供程序访问; 在 os 包外面,使用名字 os.Args ( import "os" ) | |
变量 os.Args 是一个字符串 slice ; os.Args[ i ] 获取单个命令行参数(char *argv[]) ; len(os.Args) 得到命令行参数总的数量(int argc) ; | |
os.Args 是一个切片,第一个元素是 os.Args[0] ,它是命令(可执行文件)本身的名字? | |
另外的元素是程序开始执行时的参数 | |
表达式 s[m:n] 表示一个从第 m 个到第 n-1 个元素的 slice ; 如果 m 缺失,默认值为 0 ;如果 n 缺失,默认值为 len(s) ; 所以可以将期望的 slice 简写成 os.Args[1: ] | |
切片概览 | slice 是 Go 中的基础概念,当前只需将其作为一个动态容量的顺序数组 s ; |
可以通过 s[ i ] 来访问单个元素 ; | |
通过 s[ m : n ] 来访问一段连续子区间 ; | |
数组长度用 len( s ) 表示 ; | |
与大部分编程语言一样,在 Go 中,所有的索引使用前闭后开区间 ; 即包含第一个索引,不包含最后一个索引,因为这样逻辑简单(参考 Python 切片语法) 例如,s[ m : n ] 这个子序列,包含元素 s[m] ,但不包含元素 s[n] | |
知识点: 导入多个包 | 这里有一个 UNIX echo 命令的实现,它将命令行参数输出到一行; 该实现导入两个包,使用由圆括号括起来的列表,而不是独立的 import 声明;两者都是合法的;导入的顺序是没有关系的,gofmt 工具会将其按照字母顺序表进行排序 |
代码示例1 | // echo1 输出其命令行参数 package main? // import "fmt" // import "os" import ( ? ? ? ? "fmt" ? ? ? ? "os" ) func main() { ? ? ? ? var? s ,sep? string?? ? ? ? ? for? i? :=? 1;i? <? len(os.Args);i++? { ? ? ? ? ? ? ? ? s? +=? sep? +? os.Args[ i ] ? ? ? ? ? ? ? ? sep? =? " " ? ? ? ? } ? ? ? ? fmt.Println( s ) } |
注释 | 注释以? //? 开头 所有以? //? 开头的文本是给程序员看的注释,编译器将会忽略这些文本 习惯上,在一个包声明(package 声明)前,使用注释对其进行描述 对于 main 包,注释是一个或多个完整的句子,用来对这个程序进行整体概括 |
变量声明 及初始化 | // var 关键字声明了两个 string 类型的变量 s 和 sep 变量可以在声明的时候初始化; 如果变量没有明确地初始化,变量会初始化为这个类型的空值(零值) 数字初始化为 0 ,字符串初始化为 "" , |
对于数字,Go 提供了常规的算术和逻辑操作符; 当应用于字符串时,+ 操作符对字符串的值进行追加操作,所以表达式 ? ? ? ? sep + os.Args[ i ] 表示将? sep? 和? os.Args[ i ] 追加到一起;程序中使用的语句 ? ? ? ? s? +=?? sep? +? os.Args[ i ] 是一个赋值语句,将? sep? 和? os.Args[ i ] 追加到旧的? s? 上面,并重新赋给 s ,等价于下面的语句 : ? ? ? ? s? =? s? +? sep? +? os.Args[ i ] | |
操作符? +=? 是一个赋值操作符; 每一个算术和逻辑操作符(例如 * 或者 +)都有一个对应的赋值操作符 | |
代码说明 | echo 程序会循环每次输出,但是这个版本中,我们通过反复追加来构建一个字符串; 字符串 s 一开始为空字符串 "" ,每一次循环追加一些文本;在第一次迭代后,一个空格被插入,这样当循环结束时,每个参数之间都有一个空格;这是一个二次过程,如果参数数量很大成本会比较高,不过对于 echo 程序还好 接下来会展示几个改进版本,它们会逐步处理掉低效的地方 |
循环的索引变量 i 在 for 循环开始处声明; :=? 符号用于 " 短变量声明 " ,这种语句声明一个或多个变量,并且根据初始化的值给予合适的类型(根据值推导出类型) | |
递增语句 i++ 对 i 进行加 1 ,和下面两者写法等价 ? ? ? ? i += 1 ? ? ? ? i = i + 1 对应的递减语句 i-- 对 i 进行减 1 在 Go 中,++/-- 是语句,不像其他类 C 语言一样是表达式,所以 j = i++ 是不合法的 Go 语言只支持 "k++" 和 "k--" ,不支持 "++k" 和 "--k"? | |
for 循环 | |
for 循环是 Go 里面的唯一循环语句,分为传统 for 循环和迭代 for 循环 | |
传统 for | //? 经典 for 循环 for? initialization;condition;post { } for 循环的三个组成部分两边不用圆括号; 语句块必须用花括号括起来,哪怕只有一条语句;左花括号必须和 post(后置)语句在同一行 可选的 initialization(初始化)语句在循环开始之前执行;如果初始化语句存在,它必须是一个简单的语句,比如一个简短的变量声明,一个递增或赋值语句,或者一个函数调用 condition(条件)是一个布尔表达式,在循环的每一次迭代开始前判断,如果判断结果为真,循环继续执行,判断条件为假,立刻结束循环 post 语句在循环体(花括号括起来的所有语句)之后被执行,然后 condition 被再次判断;判断结果为假则立刻结束循环 三个部分都是可以省略的 如果没有 initialization 和 post 语句,分号可以省略: // 传统的 "while" 循环 for condition { ? ? ? ? // ... } 如果条件部分都不存在: // 无限循环 ,可以通过 break 或 return 终止循环 for { ? ? ? ? // ... } |
迭代 for | 另一种形式的 for 循环在字符串或 slice 数据上迭代,迭代 for 循环 // echo2 输出其命令行参数 package main import ( ? ? ? ? "fmt" ? ? ? ? "os" ) func main() { ? ? ? ? s,sep? :=? "","" ? ? ? ? for? _,arg? :=? range? os.Args[1 : ]? { ? ? ? ? ? ? ? ? s? +=? sep? +? arg ? ? ? ? ? ? ? ? sep? =? " " ? ? ? ? } ? ? ? ? fmt.Println( s ) } // C++ for (auto c : str) { ? ? cout << c << endl; } |
每一次迭代,range 产生一对值 :索引和这个索引处元素的值(下标 、元素值) | |
在这个例子里,我们不需要 | |