golang02 基本概念

180 阅读4分钟

三、结构

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是形参名,34是调用时的传入的实数,函数返回了一个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-nilnil意味着函数运行成功,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是等价的

像forif控制语句一样,switch也可以紧跟一个简短的变量声明,一个自增表达式、赋值语
句,或者一个函数调用(译注:比其它语言丰富)。

func Signum(x int) int {
    switch {
    case x > 0:
        return +1
    default:
        return 0
    case x < 0:
        return -1
} }

forif控制语句一样,
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语言里比较特殊的是方法可以被关联到 任意一种命名类型。

接口是一种抽象类型,这种类型可以让 我们以同样的方式来处理不同的固有类型,
不用关心它们的具体实现,而只需要关注它们提 供的方法。