go官网教程A Tour of Go
中文版:http://go-tour-cn.appsp0t.com/#4
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println("Happy", math.Pi, "Day")
}
每个 Go 程序都是由包组成的。
程序运行的入口从包的 main
方法。
这个程序使用并导入了包 "fmt"
和 "math"
。
按惯例,包名与导入路径的最后一个目录一致。
这个代码用圆括号组合了导入,这是“factored”(分解因子)式导入声明。同样可以编写多个导入语句,例如:
import "fmt"
import "math"
不过通常都会用 factored 格式来使代码工整。
在导入了一个包之后,就可以用其导出的名称来调用它。
在 Go 中,首字母大写的名称是被导出的。
Foo
和 FOO
都是被导出的名称。 名称 foo
是不会被导出的。
执行代码。然后将 math.pi
改为 math.Pi
再试着执行一下。
package main import "fmt" func add(x int, y int) int {
return x + y
} func main() {
fmt.Println(add(, ))
}
函数可以有零个或多个参数。
在这个例子中,add
有两个 int
类型的参数。
注意类型声明在变量名之后。(个人认为这个可以理解)
(参考这篇关于 Go 语法定义的文章了解类型以这种形式出现的原因。)
func swap(x, y string) (string, string) {
return y, x
} func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
函数可以返回任意数量的返回值。
这个函数返回了两个字符串。
func split(sum int) (x, y int) {
x = sum * /
y = sum - x
return
} func main() {
fmt.Println(split())
}
Functions take parameters. In Go, functions can return multiple "result parameters", not just a single value. They can be named and act just like variables.
If the result parameters are named, a return
statement without arguments returns the current values of the results.
函数有参数。在 Go 中,函数可以返回多个“结果参数”,而不仅仅是一个值。它们可以像变量那样被命名和使用。
如果命名了返回值的参数,一个没有参数的 return
语句,会将当前的值作为返回值返回。
Var:
var x, y, z int
var c, python, java bool func main() {
fmt.Println(x, y, z, c, python, java)
}
var
语句声明了一个变量的列表;跟函数的参数列表一样,类型声明在最后面。
0 0 0 false false false
var x, y, z int = , ,
var c, python, java = true, false, "no!" func main() {
fmt.Println(x, y, z, c, python, java)
}
变量声明时可以包含初始值,每个变量对应一个。
如果初始值是存在的,则可以省略类型声明;变量将从初始值中获得类型。
func main() {
var x, y, z int = , ,
c, python, java := true, false, "no!" fmt.Println(x, y, z, c, python, java)
}
在一个函数里面,短赋值语句:=
可以用于替代 var
的隐式类型声明。
(:=
结构不能使用在函数外,函数外的每个语法块都必须以关键字开始。)
Constant:
const Pi = 3.14 func main() {
const World = "世界"
fmt.Println("Hello", World)
fmt.Println("Happy", Pi, "Day") const Truth = true
fmt.Println("Go rules?", Truth)
}
Constants are declared like variables, but with the const
keyword.
Constants can be character, string, boolean, or numeric values.
注意这个Println。
数值常量:
const (
Big = <<
Small = Big>>
) func needInt(x int) int { return x* + }
func needFloat(x float64) float64 {
return x*0.1
} func main() {
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
fmt.Println(needFloat(Big))
}
数值常量是高精度的值。
一个未指定类型的常量由上下文来决定其类型。
也尝试一下输出 needInt(Big)
吧 溢出
For:
func main() {
sum :=
for i := ; i < ; i++ {
sum += i
}
fmt.Println(sum)
}
Go 只有一种循环结构,for
循环。
基本的 for
循环看起来跟 C 或者 Java 中做的一样,除了没有了 ( )
之外(甚至强制不能使用它们),而 { }
是必须的。
func main() {
sum :=
for ; sum < ; {
sum += sum
}
fmt.Println(sum)
}
As in C or Java, you can leave the pre and post statements empty.
跟 C 或者 Java 中一样,前置、后置条件可以为空。
func main() {
sum :=
for sum < {
sum += sum
}
fmt.Println(sum)
}
基于这一点,你也可以省略分号: C 的 while
循环在 Go 中也是用 for
实现。
func main() {
for ; ; {
}
}
如果省略了循环条件,它会一直循环下去(译者:死循环或无限循环)。
func main() {
for {
}
}
为了避免累赘,分号也可以省略,这样一个无限循环可以被简洁地表达。
if:
import (
"fmt"
"math"
) func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
// can't use v here, though
return lim
} func main() {
fmt.Println(
pow(, , ),
pow(, , ),
)
}
在 if
的简短声明处定义的变量同样可以在对应的 else
块中使用。这点要特别注意。
Go 的基本类型有
bool string int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr byte // alias for uint8 rune // alias for int32
// represents a Unicode code point float32 float64 complex64 complex128
Structs
A struct
is a collection of fields.
(And a type
declaration does what you'd expect.)
一个结构体(struct
)就是一个成员变量的集合。
(而 type
定义跟其字面意思相符。)
type Vertex struct {
X int
Y int
} func main() {
fmt.Println(Vertex{, })
}
Struct Fields(结构体成员变量)使用点号来访问。
func main(){
v :=Vertex{,}
v.X=
fmt.Println(v.X)
}
Pointers
Go has pointers, but no pointer arithmetic.
Struct fields can be accessed through a struct pointer. The indirection through the pointer is transparent.
Go 有指针,但是没有指针运算。
结构体成员变量可以通过结构体指针来访问。通过指针的间接访问也是透明的。
type Vertex struct{
X int
Y int
}
func main(){
p := Vertex{,}
q :=&p
q.X=1e9
fmt.Println(p)
}
输出:{1000000000 2}
说明可以直接输出结构体。
Struct Literals
Struct Literals(结构体文法)表示通过结构体成员变量的值作为列表来新分配一个结构体。
使用 Name:
语法可以仅列出部分字段。(字段名的顺序无关。)
特殊的前缀 &
构造了指向结构体文法的指针。
Struct Literals
A struct literal denotes a newly allocated struct value by listing the values of its fields.
You can list just a subset of fields by using the Name:
syntax. (And the order of named fields is irrelevant.)
The special prefix &
constructs a pointer to a struct literal.
type Vertex struct {
X, Y int
} var (
p = Vertex{, } // has type Vertex
q = &Vertex{, } // has type *Vertex
r = Vertex{X: } // Y:0 is implicit
s = Vertex{} // X:0 and Y:0
) func main() {
fmt.Println(p, q, r, s)
}
{1 2} &{1 2} {1 0} {0 0}
new
函数
表达式 new(T)
分配了一个零初始化的 T
值,并返回指向它的指针。(感觉这个语法有点奇怪,()里面为类型,怎么初始化呢?
var t *T = new(T)
或
t := new(T)
The new function
The expression new(T)
allocates a zeroed T
value and returns a pointer to it.
var t *T = new(T)
or
t := new(T)
type Vertex struct {
X, Y int
} func main() {
v := new(Vertex)
fmt.Println(v)
v.X, v.Y = ,
fmt.Println(v)
}
Map
map 映射键到值。
map 必须用 make
来创建(不是 new
);一个值为 nil
的 map 是空的,并且不能赋值。
Maps
A map maps keys to values.
Maps must be created with make
(not new
) before use; the nil
map is empty and cannot be assigned to.
type Vertex struct {
Lat, Long float64
} var m map[string]Vertex func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, 74.39967,
}
fmt.Println(m["Bell Labs"])
}
map literals(map 的文法)跟struct literals(结构体文法)相似,但是键是必须的。
Maps
Map literals are like struct literals, but the keys are required.
type Vertex struct {
Lat, Long float64
} var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
} func main() {
fmt.Println(m)
}
map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
Map
如果顶层类型只有类型名的话,可以在文法的元素中省略键名。
Maps
If the top-level type is just a type name, you can omit it from the elements of the literal.
type Vertex struct {
Lat, Long float64
} var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
} func main() {
fmt.Println(m)
}
Insert or update an element in map m
:
m[key] = elem
Retrieve an element:
elem = m[key]
Delete an element:
delete(m, key)
Test that a key is present with a two-value assignment:
elem, ok = m[key]
If key
is in m
, ok
is true
. If not, ok
is false
and elem
is the zero value for the map's element type.
Similarly, when reading from a map if the key is not present the result is the zero value for the map's element type.
func main() {
m := make(map[string]int) m["Answer"] =
fmt.Println("The value:", m["Answer"]) m["Answer"] =
fmt.Println("The value:", m["Answer"]) delete(m, "Answer")
fmt.Println("The value:", m["Answer"]) v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok)
}
Slice
slice 指向数组的值,并且同时包含了长度信息。
[]T
是一个元素类型为 T
的 slice。
Slices
A slice points to an array of values and also includes a length.
[]T
is a slice with elements of type T
. (java是T【】)
func main() {
p := []int{, , , , , }
fmt.Println("p ==", p) for i := ; i < len(p); i++ {
fmt.Printf("p[%d] == %d\n",
i, p[i])
}
}
slice 可以重新切片,创建一个新的 slice 值指向相同的数组。
表达式
s[lo:hi]
表示从 lo
到 hi-1
的 slice 元素,含有两端。 因此
s[lo:lo]
是空的,而
s[lo:lo+1]
有一个元素。
func main() {
p := []int{, , , , , }
fmt.Println("p ==", p)
fmt.Println("p[1:4] ==", p[:]) // missing low index implies 0
fmt.Println("p[:3] ==", p[:]) // missing high index implies len(s)
fmt.Println("p[4:] ==", p[:])
}
跟python一样。
slice 由函数 make
创建。这会分配一个零长度的数组并且返回一个 slice 指向这个数组:
a := make([]int, 5) // len(a)=5
slice 有长度和容量。slice 的容量是底层数组可以增长的最大长度。
为了指定容量,可传递第三个参数到 make
:
b := make([]int, 0, 5)
// len(b)=0, cap(b)=5
slice 可以通过“重新切片”来扩容(增加容量):
b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:] // len(b)=4, cap(b)=4
func main() {
a := make([]int, )
printSlice("a", a)
b := make([]int, , )
printSlice("b", b)
c := b[:]
printSlice("c", c)
d := c[:]
printSlice("d", d) e :=make([]int,)
printSlice("e",e);
}
打印slice通过printSlice
function
函数也是值。
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(3, 4))
}
函数
func adder() func(int) int {
sum :=
return func(x int) int {
sum += x
return sum
}
} func main() {
pos, neg := adder(), adder()
for i := ; i < ; i++ {
fmt.Println(
pos(i),
neg(-*i),
)
}
}
并且函数是完全闭包的。
函数 adder
返回一个闭包。每个闭包被绑定到自己的 sum
变量上。
Functions
And functions are full closures.
The adder
function returns a closure. Each closure is bound to its own sum
variable.
Range
可以将值赋值给 _
来忽略键和值。
如果只需要索引值,去掉“, value
”的部分即可。
Range
You can skip the index or value by assigning to _
.
If you only want the index, drop the “, value
” entirely.
pow := make([]int, )
for i := range pow {
pow[i] = <<uint(i)
}
for _, value := range pow {
fmt.Printf("%d\n", value)
Switch
你可能已经猜到 switch
的形式了。
case 语句匹配后会自动终止,除非用 fallthrough
语句作为结尾。
import (
"fmt"
"runtime"
) func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
}
没有条件的 switch 与 switch true
一样。
这一构造使得可以用更清晰的形式来编写if-then-else。
import (
"fmt"
"time"
) func main() {
t := time.Now()
switch {
case t.Hour() < :
fmt.Println("Good morning!")
case t.Hour() < :
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}
方法
Go 没有类。然而,仍然可以在结构体类型上定义方法。
方法接收者出现在 func
关键字和方法名之间的参数中。
import (
"fmt"
"math"
) type Vertex struct {
X, Y float64
} func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
} func main() {
v := &Vertex{, }
fmt.Println(v.Abs())
}
事实上,可以对包中的任意类型定义任意方法,而不仅仅是结构体。
不能对来自其他包的类型或基础类型定义方法。
type MyFloat float64 func (f MyFloat) Abs() float64 {
if f < {
return float64(-f)
}
return float64(f)
} func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
}
接收者为指针的方法
方法可以与命名类型或命名类型的指针关联。
刚刚看到的两个 Abs
方法。一个是在 *Vertex
指针类型上,而另一个在MyFloat
值类型上。
有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。
尝试修改 Abs
的定义,同时 Scale
方法使用 Vertex
代替 *Vertex
作为接收者。
当 v
是 Vertex
的时候 Scale
方法没有任何作用。Scale
修改 v
。当 v
是一个值(非指针),方法看到的是 Vertex
的副本,并且无法修改原始值。
Abs
的工作方式是一样的。只不过,仅仅读取 v
。所以读取的是原始值(通过指针)还是那个值的副本并没有关系。
type Vertex struct {
X, Y float64
} func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
} func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
} func main() {
v := &Vertex{, }
v.Scale()
fmt.Println(v, v.Abs())
}
&{15 20} 25
去掉指针值没有变化。
接口
接口类型是由一组方法定义的。
接口类型的值可以容纳实现这些方法的任何值。
Interfaces
An interface type is defined by a set of methods.
A value of interface type can hold any value that implements those methods.
type Abser interface {
Abs() float64
} func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{, } a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser
a = v // a Vertex, does NOT
// implement Abser fmt.Println(a.Abs())
} type MyFloat float64 func (f MyFloat) Abs() float64 {
if f < {
return float64(-f)
}
return float64(f)
} type Vertex struct {
X, Y float64
} func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
一种类型通过实现那些方法来实现接口。
没有显式声明的必要。
隐式接口解藕了实现接口的包和定义接口的包:互不依赖。
也鼓励明确的接口定义,因为这样就无需找到每一个实现,并对其加上新的接口名称。
Package io 定义了 Reader
和 Writer
;但不是一定要这么做。
import (
"fmt"
"os"
) type Reader interface {
Read(b []byte) (n int, err error)
} type Writer interface {
Write(b []byte) (n int, err error)
} type ReadWriter interface {
Reader
Writer
} func main() {
var w Writer // os.Stdout implements Writer
w = os.Stdout fmt.Fprintf(w, "hello, writer\n")
}
go官网教程A Tour of Go的更多相关文章
- [pytorch] 官网教程+注释
pytorch官网教程+注释 Classifier import torch import torchvision import torchvision.transforms as transform ...
- Unity 官网教程 -- Multiplayer Networking
教程网址:https://unity3d.com/cn/learn/tutorials/topics/multiplayer-networking/introduction-simple-multip ...
- MongoDB 官网教程 下载 安装
官网:https://www.mongodb.com/ Doc:https://docs.mongodb.com/ Manual:https://docs.mongodb.com/manual/ 安装 ...
- ECharts概念学习系列之ECharts官网教程之在 webpack 中使用 ECharts(图文详解)
不多说,直接上干货! 官网 http://echarts.baidu.com/tutorial.html#%E5%9C%A8%20webpack%20%E4%B8%AD%E4%BD%BF%E7%94% ...
- ECharts概念学习系列之ECharts官网教程之自定义构建 ECharts(图文详解)
不多说,直接上干货! 官网 http://echarts.baidu.com/tutorial.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9E%84%E5%BB%BA%2 ...
- KnockoutJs官网教程学习(一)
这一教程中你将会体验到一些用knockout.js和Model-View-ViewModel(MVVM)模式去创建一个Web UI的基础方式. 将学会如何用views(视图)和declarative ...
- scrapy1_官网教程
https://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/tutorial.html 本篇文章主要介绍如何使用编程的方式运行Scrapy爬虫. 在开始本文之 ...
- Unreal Engine 4官网教程
编辑器纵览 https://www.unrealengine.com/zh-CN/blog/editor-overview 虚幻编辑器UI布局概述 https://www.unrealengine.c ...
- Postman 官网教程,重点内容,翻译笔记,
json格式的提交数据需要添加:Content-Type :application/x-www-form-urlencoded,否则会导致请求失败 1. 创建 + 测试: 创建和发送任何的HTTP请求 ...
随机推荐
- C primer plus 读书笔记第十四章
这一章主要介绍C语言的结构和其他数据形式,是学习算法和数据结构的重点. 1.示例代码 /*book.c -- 仅包含一本书的图书目录*/ #include <stdio.h> #defin ...
- htaccess URL重写rewrite与重定向redirect(转)
1. 将 .htm 页面映射到 .php 1 Options +FollowSymlinks 2 RewriteEngine on 3 RewriteRule ^(.*)\.htm$ $1.php [ ...
- padding与margin的差别
之前一直没有搞懂android:padding和android:layout_margin的差别,事实上概念非常easy,padding是站在父view的角度描写叙述问题,它规定它里面的内容必须与这个 ...
- C++类的成员函数(在类外定义成员函数、inline成员函数)
类的成员函数(简称类函数)是函数的一种,它的用法和作用和前面介绍过的函数基本上是一样的,它也有返回值和函数类型,它与一般函数的区别只是:它是属于一个类的成员,出现在类体中.它可以被指定为private ...
- JNI之有必要的优化设计
对象指针的保存 在上一章中,c函数中将会获取的一些值,例如:FieldID.MethodID.jclass等数据.这些数据如果定义在函数内部,在函数返回时就会丢失.很多时候,在java与c的多次交互中 ...
- python摇骰子猜大小的小游戏
#小游戏,摇筛子押大小的小游戏玩家初始有1000块钱,可以压大压小作为赌注 import random #定义摇筛子的函数: def roll_dice(number = 3,points = Non ...
- ubuntu wine卸载程序并删除图标
卸载ubuntu 下用wine安装的程序,可以用wine uninstaller命令,打开 添加/删除程序界面,进行删除程序操作:
- openstack初探
一 .openstack三大核心功能: 计算--Nova.存储--Cinder.网络--Neutron. Nova:提供了计算资源的管理,可以管理跨服务网络的VM实例.还提供对多种Hypervisor ...
- 【转】vue基础学习
1.基本绑定: new Vue( { el:'#elID', data:{ // data obj ...
- XlFileFormat
-----转载:http://hi.baidu.com/liu_haitao/item/900ddb38979188c22f8ec26e 18 XlFileFormat.xlAddIn Microso ...