相比很多其它流行语言,Go的语法相对简洁。 此篇文章将介绍编程中常用的代码元素,并展示一份简单的Go程序代码,以便让刚开始学Go编程的程序员对Go代码结构有一个大概的印象。
编程和程序代码元素
简单来讲,编程可以看作是以各种方式控制和组合计算机运行中的各种操作,以达到各种各样的目的。 一个操作可能从一个硬件设备读取、或者向一个硬件设备写入一些数据,从而完成一个特定的任务。 对于现代计算机来说,最基本的操作是底层计算机指令,比如CPU和GPU指令。 常见的硬件设备包括内存、磁盘、网卡、显卡,显示器、键盘和鼠标等。
直接操控底层计算机指令进行编程是非常繁琐和容易出错的。 高级编程语言通过对底层指令进行一些封装和对数据进行一些抽象,从而使得编程变得直观和易于理解。
在流行高级编程语言中,一个操作通常是通过函数(function)调用或者使用操作符(operator)运算来完成的。 大多数高级编程语言都支持一些条件和循环控制语句。 这些条件和循环控制语句可以看作是特殊的操作。 它们的语法接近于人类语言,因此一个人写的代码很容易被其他人理解。
在大多数高级编程语言中,数据通常被抽象为各种类型(type)和值(value)。 一个类型可以看作是值的模板。一个值可以看作是某个类型的实例。 大多数编程语言支持自定义类型和若干预定义类型(即内置类型)。 一门语言的类型系统可以说是这门语言的灵魂。
编程中常常会使用大量的值。 一些在编码阶段可确定的值可以用它们的字面形式(literal,即字面量)来表示。 为了编程灵活和不易出错,其它的值一般使用变量(variable)和(有名)常量(named constant)来表示。
在《Go语言101》中,有名字的函数、有名字的值(包括变量和有名常量)、以及定义类型和类型别名将被统称为资源。 资源名必须为标识符(identifier)。
高级编程语言代码将被编译器或者解释器转换为底层机器码进行执行。 为了帮助编译器和解释器解析高级语言代码,一些单词将被用做关键字(keyword)。 这些单词不能被当做标识符使用。
很多现代高级语言使用包(package)来组织代码。 一个包必须引入(import)另一个包才能使用另一个包中的公有(导出的)资源。 包名和包的引入名也都必须是标识符。
尽管高级编程语言代码比底层机器指令友好和易懂,我们还是需要一些注释来帮助自己和其他程序员理解我们所写的代码。 在下一节的程序示例中,我们可以看到很多注释。
一个简单的Go示例程序
为了对各种代码元素有一个更清楚的认识,让我们来看一个简短的Go示例程序。
和很多其流行语言一样,Go使用//
来起始一个行注释,使用一个/*
和*/
对来包裹一个块注释。
下面是这个Go示例程序。请注意阅读其中的注释。程序之后有更多解释。
package main // 指定当前源文件所在的包名
import "math/rand" // 引入一个标准库包
const MaxRand = 16 // 声明一个有名整型常量
// 一个函数声明
/*
StatRandomNumbers生成一些不大于MaxRand的非负
随机整数,并统计和返回小于和大于MaxRand/2的随机数
个数。输入参数numRands指定了要生成的随机数的总数。
*/
func StatRandomNumbers(numRands int) (int, int) {
// 声明了两个变量(类型都为int,初始值都为0)
var a, b int
// 一个for循环代码块
for i := 0; i < numRands; i++ {
// 一个if-else条件控制代码块
if rand.Intn(MaxRand) < MaxRand/2 {
a = a + 1
} else {
b++ // 等价于:b = b + 1
}
}
return a, b // 此函数返回两个结果
}
// main函数,或主函数,是一个程序的入口函数。
func main() {
var num = 100
// 调用上面声明的StatRandomNumbers函数,
// 并将结果赋给使用短声明语句声明的两个变量。
x, y := StatRandomNumbers(num)
// 调用两个内置函数(print和println)。
print("Result: ", x, " + ", y, " = ", num, "? ")
println(x+y == num)
}
将上面的程序代码存盘到一个名为basic-code-element-demo.go
的文件中并使用下列命令运行此程序:
$ go run basic-code-element-demo.go
Result: 46 + 54 = 100? true
在上面的示例程序中,单词package
、import
、const
、func
、var
、for
、if
、else
和return
均为关键字。
其它大多数单词均为标识符。
请阅读关键字和标识符以获得更多关于关键字和标识符的信息。
四个int
(一个在第15行,另三个在第13行)
表示内置基本类型int
。int类型是Go中的基本整数类型之一。
第5行中的16
、第17行中的0
、
第20行中的1
以及第30行的100
均为整型字面量。
第35行的"Result: "
是一个字符串字面量。
请阅读基本类型和它们的字面量表示以获取更多关于基本类型和它们的字面量的信息。
Go中的非基本类型(均为组合类型)将在以后的其它文章中介绍和解释。
第20行是一个赋值语句。
第5行声明了一个有名常量,叫做MaxRand
。
第15行和第30行使用标准变量声明语句声明了三个变量。
第17行的变量i
以及第33行的变量x
和y
是使用变量短声明语句声明的。
变量a
和b
在声明的时候被指定为int
类型。
编译器会自动推导出变量i
、num
、x
和y
的类型均为int
类型,因为它们的初始值都是整型字面量表示的。
请阅读常量和变量以获取什么是类型不确定值、类型推导、赋值、以及如何声明变量和有名常量。
上面的示例程序中使用了很多操作符,比如第17和19行的小于比较符<
,第36行的等于比较符==
,还有第20和36行的加法运算符+
。
第35行中的+
不是一个运算符,它是一个字符串字面量中的一个字符。
一个使用操作符的操作中涉及到的值称为操作值(有时也可称为运算数)。
请阅读常用操作符以获取更多关于操作符的信息。
更多操作符将在后续其它文章中介绍。
第35和35行调用了两个内置函数print
和println
。
从第13行到第26行声明的函数StatRandomNumbers
在第33行被调用。
第19行也调用了一个函数 Intn
。
这个函数声明在math/rand
标准库包中。
请阅读函数声明及函数调用以获取更多关于函数声明及函数调用的信息。
(注意,一般print
和println
这两个内置函数并不推荐使用。
在正式的项目中,我们应该尽量使用fmt
标准库包中声明的相应函数。
《Go语言101》只在开始的几篇文章中使用了这两个函数。)
第1行指定了当前源文件所处的包的名称。
一个Go程序的主函数(main
函数)必须被声明在一个名称为main
的包中。
第3行引入了math/rand
标准库包,并以rand
做为引入名。
在这个包中声明的Intn
函数将在第19行被调用。
请阅读代码包和包引入,以获取更多关于代码包和包引入的信息。
表达式、语句和简单语句一文中介绍了什么是表达式和语句。特别地,此文列出了所有的简单语句类型。 在Go代码中,各种流程控制代码块中的某些部分必须为简单语句,某些部分必须为表达式。
StatRandomNumbers
函数的声明体中使用了两个流程控制代码块。
其中一个是for
循环代码块,它内嵌了另外一个代码块。
另外一个代码块是一个if-else
条件控制代码块。
请阅读基本流程控制语法以获取更多关于流程控制代码块的信息。
更多的特殊的流程控制代码块将在以后的其它文章中介绍。
空行常常用来增加代码的可读性。 上面的程序中也包涵了很多注释,但它们大多是为了Go初学者快速理解的目的而加入的。 我们应该尽量使代码自解释,只在确实需要解释的地方进行注释。
关于代码断行
{
and }
来形成一个显式代码块。但是在Go代码中,编码样式风格有一些限制。
比如,很多左大括号{
不能被放到下一行。
如果,上面的StatRandomNumbers
被修改成如下所示,则上面的示例程序将编译不通过。
func StatRandomNumbers(numRands int) (int, int)
{ // 编译错误:语法错误
var a, b int
for i := 0; i < numRands; i++
{ // 编译错误:语法错误
if rand.Intn(MaxRand) < MaxRand/2
{ // 编译错误:语法错误
a = a + 1
} else {
b++
}
}
return a, b
}
一些程序员不是很喜欢这些限制。但是这些限制有两个好处:
- 它们使得Go程序编译得非常快。
- 它们使得不同的Go程序员编写的代码风格类似,从而一个Go程序员写的代码很容易被另一个程序员看懂。
我们可以阅读代码断行规则一文以获取更多关于代码换行规则的细节。在目前,我们最好避免将左大括号放在下一行。 或者说,每行的非空起始字符不能是左大括号(但是,请记住,这不是一个普适的规则)。