Go语言课程笔记 Go Course Day 1: Basic

来源:互联网 发布:网络运维与管理 改名 编辑:程序博客网 时间:2024/05/28 06:06

该笔记基于Go语言安装目录下doc文件夹中的GoCourse文档,原文将以引用格式部分包含本文中。

P14 Source is UTF-8.

P15 Literals

C-like but numbers require no signedness or size
markings (more about this soon)
23
0x0FF
1.234e7
C-like strings, but Unicode/UTF-8.
Raw strings
`/n/.abc/t/` == //n//.abc//t//

Go的Literal(字面值常量?)保持了类C的风格,但是对于数字而言,不需要添加用于标注型别的的后缀,而对于字符串,则出现了不带转义的Raw string,注意Raw string的两端不是单引号,而是~下面那个字符。

P16 Syntax overview
Basically C-like with reversed types and declarations,
plus keywords to introduce each type of declaration.
var a int
var b, c *int // note difference from C
var d []int
type S struct { a, b int }
Basic control structures are familiar:
if a == b { return true } else { return false }
for i = 0; i < 10; i++ { ... }

嗯……声明变量似乎是个大的障碍,不是一两天能习惯的。至于控制结构,省了两对括号而已,如果积习难改,写上当然也可以。但是必须注意执行部分只有一条语句时在多数类C语言中可以省略的大括号现在是必须的。

Numeric types
Numeric types are built in, will be familiar:

int

uint

float

int8

uint8 = byte

 

int16

uint16

 

int32

uint32

float32

int64

uint64

float64

Also uintptr, an integer big enough to store a pointer.
These are all distinct types; int is not int32 even on
a 32-bit machine.

整型细分到这种程度实在出人意料,但由此不需要再猜测平台上int到底是几位,直接用指定位数的类型就可以。但不同的型别从此无法直接比较,因此如果你顺手写出如下代码,编译器就会直接报错(Go的编译器默认设置似乎异常严格,甚至你import一个包或定义了一个变量没有使用它都会报错)

  1: var i int32 = 0
  2: max := 1
  3: if i < max { ... }

至于int is not int32这种说法,大抵是指它们属于不同类别的东西,但两者在32位平台上(至少我的PC上)的取值范围确实是一致的(-2147483648 到 2147483647),而在我的64位平台上,int的取值范围也和int32一致(那么它们的区别何在?在基础课程中,这个疑问会被搁置吗)。

Go的运算符和C大同小异,^替代了~诸如此类。但是需要注意的是

++ and -- are not expression operators
(x++ is a statement, not an expression;
*p++ is (*p)++ not *(p++))

事实上,几乎凭本能写出的语句可能是错的,比如

  1: if 0 < x++ { ... }

至于数值间的类型转换,愿上帝保佑我习惯吧。

另外,对于常量表达式的精度,Go的处理似乎有点夸张

// high precision:
const Ln2
= 0.693147180559945309417232121458/
176568075500134360255254120680009
const Log2E
= 1/Ln2 // accurate reciprocal
Representation is "big enough" (1024 bits now).

关于变量声明,老实说,我并不明白为什么一定要有个var关键字

Variable declarations are introduced by var.
They may have a type or an initialization expression;
one or both must be present.

既然one or both must be present,那么声明语句就绝对能由编译器推断出来,也就是说,var关键字存在的唯一理由只是防止程序员因为打字错误而定义一个莫须有的新变量而已(或者把声明语句错写成赋值)。至于为什么把型别放在变量名后面,文档做了一大堆的解释……遗憾的是我没看出个所以然来:(

但是新的短声明符号想必会受欢迎

The := "short declaration"
Within functions (only), declarations of the form
var v = value
can be shortened to
v := value

Go的文档称这是另一个把型别放在变量名后面的理由(反正Go的意思就是报上名来,不过老子不关心你是虾米玩意儿)。注意,这个符号是支持平行赋值的(等号也支持)。

当然,var关键字的另一个用法是和括号一起,一次“批发”N个变量,这一点和const关键字是相同的

const (
    Monday, Tuesday, Wednesday = 1, 2, 3;
    Thursday, Friday, Saturday = 4, 5, 6;
)

在定义const时,可以用到iota计数器。Well,现在你拥有了enum。

Constant declarations can use the counter iota,
which starts at 0 in each const block and increments
at each semicolon.
Shorthand: Previous type and expressions repeat.
const (
    loc0, bit0 uint32 = iota, 1<    loc1, bit1; // 1, 2
    loc2, bit2; // 2, 4
)

控制结构和C也是类似的。注意在for循环中不允许使用C风格的逗号分隔符,也就是说,当你需要表示i = 1, j = 2时应该用i, j = 1, 2这样的平行赋值风格来代替。

switch的表达和C的差别就大了

But there are important differences:
- expressions need not be constants or even ints.
- no automatic fall through
- instead, lexically last statement can be fallthrough
- multiple cases can be comma-separated
switch count%7 {
    case 4,5,6: error();
    case 3: a *= v; fallthrough;
    case 2: a *= v; fallthrough;
    case 1: a *= v; fallthrough;
    case 0: return a*v;
}

在上述的例子里,如果count%7的结果是2,那么在运行完case 2的a *= v后,将再次运行a *= 2,最后返回a*v。我猜,这样的语法……没有人会因为忘记break而闯祸了。增强后的switch甚至成为了if-else链更为优雅的替代品

The expressions can be any type and a missing
switch expression means true. Result: if-else chain:
a, b := x[i], y[j];
switch {
    case a < b: return –1
    case a == b: return 0
    case a > b: return 1
}
or
switch a, b := x[i], y[j]; { ... }

或许这才是Go语言中switch真正的战场:替代一长串的if-else,或许正是因为这个原因,break才被迫在switch中退场,而加入了一个素未谋面的fallthrough。

A function can return multiple values. If so, the
return types are a parenthesized list.
func MySqrt(f float) (float, bool) {
    if f >= 0 { return math.Sqrt(f), true }
    return 0, false
}
func MySqrt(f float) (v float, ok bool) {
    if f >= 0 { v,ok = math.Sqrt(f),true }
    else { v,ok = 0,false }
return v,ok
}

多值返回……感谢真主,赐予我多值返回:)不过可以预见,多值返回会对程序员处理错误的方式及风格起到多大的影响。拭目以待。对于定义了返回类型或返回变量的function而言,单独的return意味着将返回变量的当前值返回。与其它高级动态语言不通,这里仍然必须显式的写出return语句。

在function中,可以使用defer关键字

Defer
The defer statement executes a function (or method)
when the enclosing function returns.

嗯,我想起了finally语句。不过不太一样。以下结果的输出是1 0 2

  1: package main
  2: 
  3: func f(v int)(int) {
  4:     v++
  5:     println(v)
  6:     return
  7: }
  8: 
  9: func main() {
 10:     i := 0
 11:     defer f(f(i)) // f(i) evaluated, print 1; function return 2, but deferred
 12:     println(i) // 0
 13: }

……一翻到下一页就看到了更好的例程,郁闷。不过,这种立即评估、推迟返回的语法带有明显的黑客习性,是不是会造成新的风格混乱还很难说。但可以肯定的是,Joel Spolsky一定不会喜欢这种语法。他会告诉你,这里带有一种无形的goto……相关的东西没有集中在一起……等等,如果你不相信,他会继续告诉你说Raymond Chen是站在他一边的。

在function中,还有类似于(就是?)匿名函数的语法:

func f() {
    for i := 0; i < 10; i++ {
        g := func(i int) { fmt.Printf("%d",i) };
        g(i);
    }
}

U GOT IT,我们有闭包用了!

For clients (importers) of the package, names must
be upper case to be visible

!!对我来说这样的做法太……奇怪了。用大小写来区分public还是private??和Python空格有得一拼。

2) An init() function, as many as one per source file.
Package dependency guarantees correct execution
order.
Initialization is always single-threaded.

有点像Java里的static {}。

在组织Go程序的源代码结构时,可以把每个文件夹归为一个package,每个package中的源文件都需要一起进行编译(Test代码除外)。

Testing
To test a package, write a set of Go source files
within the same package; give the files names of the
form *_test.go.
Within those files, global functions with names
starting Test[^a-z]* will be run by the testing tool,
gotest. Those functions should have signature
func TestXxxx(t *testing.T)
The "testing" package provides support for logging,
error reporting.

命名规则再次扮演重要角色。

第一课结束,练习题是Fibonacci数列,第一次写的时候没有做缓存,结果弄成了喷涂算法的结果,立马加上map,匀速前进!GoBuild果然很好用,不用写MakeFile就能自动搞定一起……除了测试,真正用上了才发现,-t参数居然还是“尚未实现”状态……
PS:64位无符号整数,真TNND大啊。

  1: package exercise
  2: import "os"
  3: 
  4: var m map[int]uint64
  5: 
  6: func Fibonacci(i int) (v uint64) {
  7:   if i < 1 {
  8:     os.Exit(1)
  9:   }
 10:   
 11:   if m == nil { m = make(map[int]uint64) }
 12:   v = m[i]
 13:   if v > 0 { return }
 14: 
 15:   switch i {
 16:     case 1, 2: v = uint64(i)
 17:     default: v = Fibonacci(i - 1) + Fibonacci(i - 2)
 18:   }
 19:   m[i] = v
 20:   return
 21: }

******


Quote of the day:
Read, every day, something no one else is reading. Think, every day, something no one else is thinking. Do, every day, something no one else would be silly enough to do. It is bad for the mind to be always part of unanimity. - Christopher Morley