三、结构
3.1、for循环
循环体标准结构:
for initialization; condition; post {
// zero or more statements
}
initialization语句是可选的,在循环开始前执行。
initalization如果存在,必须是一条简单语句 (simple statement),
即,短变量声明、自增语句、赋值语句或函数调用。
condition 是一 个布尔表达式(boolean expression),其值在每次循环迭代开始时计算。
如果为 true 则执 行循环体语句。
post 语句在循环体执行结束后执行,之后再次对 condition 求值。
condition 值为 false 时,循环结束。
for i := 1; i < len(os.Args); i++ {
i := 1; initialization语句;短变量声明
i < len(os.Args);condition语句,这是条件,<len(os.Args)前都为true,持续执行
i++;post语句,持续循环逻辑并执行循环
如果省略 initialization 和 post ,分号也可以省略:
for condition {
如果连 condition 也省略:
for {\
其他例子:
for t := 0.0; t < cycles*2*math.Pi; t += res {
3.2、for循环在range上的形式
for _, arg := range os.Args[1:] {
空标识符 (blank identifier),即 _ (也就是下划线)
这里arg为内容,若不使用_,则_值为index
即os.Args[1] os.Args[2] os.Args[3] ...
常量
常量声明和变量声明一般都会出现在包级别,所以这些 常量在整个包中都是可以共享的,
或者也可以把常量声明定义在函数体内部,那么这种常 量就只能在函数体内用。
目前常量声明的值必须是一个数字值、字符串或者一个固定的 boolean值。
复合声明
复合类型的一种写法:
var palette = []color.Color{color.White, color.Black}
anim := gif.GIF{LoopCount: nframes}
前者生成的是一个slice切片,后者生成的 是一个struct结构体
anim := gif.GIF{LoopCount: nframes}
这种写法会生成一个struct变量,并且其内部变量LoopCount字段会被设置为nframes
struct内部的变量可以以一个点(.)来进行访问
anim.Delay = append(anim.Delay, delay)
anim.Image = append(anim.Image, img)
函数
函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。
func name(parameter-list) (result-list) {
body
}
func hypot(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
x和y是形参名,3和4是调用时的传入的实数,函数返回了一个float64类型的值。 返回值也可以 像形式参数一样被命名。
在这种情况下,每个返回值被声明成一个局部变量,并根据该返回 值的类型,将其初始化为0。 如果一个函数在声明时,包含返回值列表,该函数必须以 return 语句结尾,除非函数明显无法运行到结尾处。例如函数在结尾时调用了panic异常或函数中存 在无限循环。
一个函数的声明由一个函数名字、参数列表(由函数的调用者提供参数变量的具体值)、一 个可选的返回值列表和包含函数定义的函数体组成。
如果函数没有返回值,那么返回值列表 是可以省略的。如果函数返回一个无名变量或者 没有返回值,返回值列表的括号是可以省略的。如果一个函数声明不包括返回值列表,那么 函数体执行完毕后,不会返回任何值。
执行函数从函数的第一个语句开始,依次顺序执行直到遇到return返回语句,
如果 没有返回语句则是执行到函数末尾,然后返回到函数调用者。
func countLines(f *os.File, counts map[string]int) {
func fetch(url string, ch chan<- string) {
func lissajous(out io.Writer) {
func main() {
func fetch(url string, ch chan<- string) {
func handler(w http.ResponseWriter, r *http.Request) {
func fToC(f float64) float64 {
func f() *int {
func incr(p *int) int {
func delta(old, new int) int { return new - old }
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
func init() {
形参、返回参 支持同类型声明简写
func add(x int, y int) int {return x + y}
func sub(x, y int) (z int) {z=x-y;return}
func first(x int, _ int) int { return x }
func zero(int, int) int {return0}
函数递归
函数体内,自己调用函数自身
函数多返回值
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
}
调用方法
links, err := findLinks(url)
links, _ := findLinks(url) // errors ignored
一个函数内部可以将另一个有多返回值的函数作为返回值
func findLinksLog(url string) ([]string, error) {
log.Printf("findLinks %s", url)
return findLinks(url)
}
debug时,可以将一个返回多参数的函数作为该函数的参数
log.Println(findLinks(url))
links, err := findLinks(url)
log.Println(links, err)
准确的变量名可以传达函数返回值的含义
func Size(rect image.Rectangle) (width, height int)
func Split(path string) (dir, file string)
func HourMinSec(t time.Time) (hour, minute, second int)
如果要严格返回约定的返回参数,可以省略参数名
func CountWordsAndImages(url string) (words, images int, err error) {
if err != nil {
return
}
这里return 等价 return words, images, err
但不要一个函数内滥用,避免各自return 结果实际有可能不一致导致代码难以理解排错
简单可控的函数错误返回
可以用ok
value, ok := cache.Lookup(key)
if !ok {
// ...cache[key] does not exist...
}
内置的error是接口类型,error类型可能是nil或者non-nil。nil意味着函数运行成功,non-nil表示失 败
函数错误处理
1 直接返回
resp, err := http.Get(url)
if err != nil{
return nil, err
}
2 包装错误
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url,err)
}
3 向main输出错误,中断程序
// (In function main.)
if err := WaitForServer(url); err != nil {
fmt.Fprintf(os.Stderr, "Site is down: %v\n", err)
os.Exit(1)
}
if err := WaitForServer(url); err != nil {
log.Fatalf("Site is down: %v\n", err)
}
4 输出错误,不中断
if err := Ping(); err != nil {
log.Printf("ping failed: %v; networking disabled",err)
}
if err := Ping(); err != nil {
fmt.Fprintf(os.Stderr, "ping failed: %v; networking disabled\n", err)
}
5 或略错误
dir, err := ioutil.TempDir("", "scratch")
if err != nil {
return fmt.Errorf("failed to create temp dir: %v",err)
}
// ...use temp dir...
os.RemoveAll(dir) // ignore errors; $TMPDIR is cleaned periodically
结构体
if判断
if 语句条件两边也不加括号,但是主体部分需要加。
if 语句 的 else 部分是可选的,在 if 的条件为 false 时执行。
Go语言允许这样的一个简单的语句结果作为 循环的变量声明出现在if语句的最前面,这一点对错误处理很有用处。
if 表达式[可选];判断条件;
如:短变量声明;判断条件
if err := r.ParseForm(); err != nil {
等价
err := r.ParseForm()
if err != nil {
log.Print(err)
}
if n > 1 {
http.HandleFunc
handler := func(w http.ResponseWriter, r *http.Request) {
lissajous(w)
}
http.HandleFunc("/", handler)
等价
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
lissajous(w)
})
switch
完整结构体:
switch coinflip() {
case "heads":
heads++
case "tails":
tails++
default:
fmt.Println("landed on edge!")
}
switch还可以不带操作对象时默认用true值代替
将每个case的表达式和true值进行比较
这种形式叫做无tag switch(tagless switch);这和switch true是等价的
像for和if控制语句一样,switch也可以紧跟一个简短的变量声明,一个自增表达式、赋值语
句,或者一个函数调用(译注:比其它语言丰富)。
func Signum(x int) int {
switch {
case x > 0:
return +1
default:
return 0
case x < 0:
return -1
} }
像for和if控制语句一样,
switch也可以紧跟一个简短的变量声明,一个自增表达式、赋值语句,或者一个函数调用
命名类型:struct
type Point struct {
X, Y int
}
var p Point
类型
type 类型名字 底层类型
type Celsius float64 // 摄氏温度
type Fahrenheit float64 // 华氏温度
对于每一个类型T,都有一个对应的类型转换操作T(x),用于将x转为T类型
(如果T是 指针类型,可能会需要用小括弧包装T,比如 (*int)(0) )
Fahrenheit(c*9/5 + 32)
return Celsius((f - 32) * 5 / 9)
命名类型还可以为该类型的值定义新的行为。这些行为表示为一组关联到该类型的函数集
合,我们称为类型的方法集。
下面的声明语句,Celsius类型的参数c出现在了函数名的前面,表示声明的是Celsius类型的 一个叫名叫String的方法,该方法返回该类型对象c带着°C温度单位的字符串:
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
方法和接口:
方法是和命名类型关联的一类函数。
Go语言里比较特殊的是方法可以被关联到 任意一种命名类型。
接口是一种抽象类型,这种类型可以让 我们以同样的方式来处理不同的固有类型,
不用关心它们的具体实现,而只需要关注它们提 供的方法。