Go by Example 中文 学习笔记

今天(2021年6月3日)早上准备写一道力扣简单题开启今天的时候忽然想要不也用go写一下,刷题同时顺带也发展一下go的技术栈,于是从早上到现在(20点09分)题也没刷,其他啥事也没干,就一直在看网上go的基础语法教程入门。顺带整理这个笔记。

整理着发现太费时了,不如简单粗暴点参考力扣GO题解的代码直接开刷,碰到猜不透的代码再直接Google

下面整理的主要是我认为对力扣刷题有用的小节

目录

变量

常量

const声明常量

数值型常量是没有确定的类型的

变量显式类型转化

根据上下文需要为数值常量给定类型

For循环

for是 Go 中唯一的循环结构

有三个基本使用方式

单个循环条件式,最常用

经典的初始化/条件/后续形式

不带条件的 `for`,循环体内使用 `break` 或者 `return` 来跳出循环

if/else 分支

在 Go 中,你可以不适用圆括号,但是花括号是需要的

在条件语句之前可以有一个语句;任何在这里声明的变量都可以在所有的条件分支中使用。

Go 里没有三目运算符,所以即使你只需要基本的条件判断,你仍需要使用完整的 if 语句。

分支结构

在一个 case 语句中,你可以使用逗号来分隔多个表达式。

不带表达式的 switch 是实现 if/else 逻辑的另一种方式

数组

创建数组

一行内初始化一个数组

创建二维数组并初始化

切片

Slice数据类型

创建Slice

Slice的append操作

Slice的“切片”操作

在一行代码中声明并初始化一个 slice 变量

Slice组成多维数据结构

关联数组(就是java中的map,只是另一个叫法)

创建空map

设置、打印、获取、删除、键值对

从map中取值获取到的两个返回值

在同一行申明和初始化一个新的map的语法

Range 遍历

range迭代slice

range迭代map

range迭代字符串

函数

多返回值

如何定义多返回值函数

通过多赋值操作获取多个返回值

通过空白定义符'_'仅获取返回值的一部分

变参函数

常见的变参函数:fmt.Println

如何将slice作为变参函数调用

闭包

递归(和其他语言一样)

指针(和C一样,没啥新变化)

结构体

在结构体类型中定义方法 

接口

排序函数的使用

使用函数自定义排序

组合函数(将函数作为参数传递并使用,类似C的函数指针)

字符串函数

Printf格式化输出

正则表达式


// Go 拥有各值类型,包括字符串,整形,浮点型,布尔
// 型等。下面是一些基本的例子。

package main

import "fmt"

func main() {

    // 字符串可以通过 `+` 连接。
    fmt.Println("go" + "lang")

    // 整数和浮点数
    fmt.Println("1+1 =", 1+1)
    fmt.Println("7.0/3.0 =", 7.0/3.0)

    // 布尔型,还有你想要的逻辑运算符。
    fmt.Println(true && false)
    fmt.Println(true || false)
    fmt.Println(!true)
}



 
// $ go run values.go
// golang
// 1+1 = 2
// 7.0/3.0 = 2.3333333333333335
// false
// true
// false

变量

// 在 Go 中,_变量_ 被显式声明,并被编译器所用来
// 检查函数调用时的类型正确性

package main

import "fmt"

func main() {

    // `var` 声明 1 个或者多个变量。
    var a string = "initial"
    fmt.Println(a)

    // 你可以申明一次性声明多个变量。
    var b, c int = 1, 2
    fmt.Println(b, c)

    // Go 将自动推断已经初始化的变量类型。
    var d = true
    fmt.Println(d)

    // 声明变量且没有给出对应的初始值时,变量将会初始化为
    // _零值_ 。例如,一个 `int` 的零值是 `0`。
    var e int
    fmt.Println(e)

    // `:=` 语句是申明并初始化变量的简写,例如
    // 这个例子中的 `var f string = "short"`。
    f := "short"
    fmt.Println(f)
}




输出:
initial
1 2
true
0
short

常量

const声明常量

数值型常量是没有确定的类型的

变量显式类型转化

根据上下文需要为数值常量给定类型

// Go 支持字符、字符串、布尔和数值 _常量_ 。
package main

import "fmt"
import "math"

// `const` 用于声明一个常量。
const s string = "constant"

func main() {
    fmt.Println(s)

    // `const` 语句可以出现在任何 `var` 语句可以出现
    // 的地方
    const n = 500000000

    // 常数表达式可以执行任意精度的运算
    const d = 3e20 / n
    fmt.Println(d)

    // 数值型常量是没有确定的类型的,直到它们被给定了一个
    // 类型,比如说一次显示的类型转化。
    fmt.Println(int64(d))

    // 当上下文需要时,一个数可以被给定一个类型,比如
    // 变量赋值或者函数调用。举个例子,这里的 `math.Sin`
    // 函数需要一个 `float64` 的参数。
    fmt.Println(math.Sin(n))
}





输出
constant
6e+11
600000000000
-0.28470407323754404

For循环

for是 Go 中唯一的循环结构

有三个基本使用方式

  • 单个循环条件式,最常用

  • 经典的初始化/条件/后续形式

  • 不带条件的 `for`,循环体内使用 `break` 或者 `return` 来跳出循环

// `for` 是 Go 中唯一的循环结构。这里有 `for` 循环
// 的三个基本使用方式。

package main

import "fmt"

func main() {

    // 最常用的方式,带单个循环条件。
    i := 1
    for i <= 3 {
        fmt.Println(i)
        i = i + 1
    }

    // 经典的初始化/条件/后续形式 `for` 循环。
    for j := 7; j <= 9; j++ {
        fmt.Println(j)
    }

    // 不带条件的 `for` 循环将一直执行,直到在循环体内使用
    // 了 `break` 或者 `return` 来跳出循环。
    for {
        fmt.Println("loop")
        break
    }
}





输出:
1
2
3
7
8
9
loop

if/else 分支

在 Go 中,你可以不适用圆括号,但是花括号是需要的

在条件语句之前可以有一个语句;任何在这里声明的变量都可以在所有的条件分支中使用。

Go 里没有三目运算符,所以即使你只需要基本的条件判断,你仍需要使用完整的 if 语句。

// `if` 和 `else` 分支结构在 Go 中当然是直接了当的了。

package main

import "fmt"

func main() {

    // 这里是一个基本的例子。
    if 7%2 == 0 {
        fmt.Println("7 is even")
    } else {
        fmt.Println("7 is odd")
    }

    // 你可以不要 `else` 只用 `if` 语句。
    if 8%4 == 0 {
        fmt.Println("8 is divisible by 4")
    }

    // 在条件语句之前可以有一个语句;任何在这里声明的变量
    // 都可以在所有的条件分支中使用。
    if num := 9; num < 0 {
        fmt.Println(num, "is negative")
    } else if num < 10 {
        fmt.Println(num, "has 1 digit")
    } else {
        fmt.Println(num, "has multiple digits")
    }
}

// 注意,在 Go 中,你可以不适用圆括号,但是花括号是需
// 要的。




输出:
7 is odd
8 is divisible by 4
9 has 1 digit

分支结构

在一个 case 语句中,你可以使用逗号来分隔多个表达式。

   case time.Saturday, time.Sunday:
        fmt.Println("it's the weekend")

不带表达式的 switch 是实现 if/else 逻辑的另一种方式

 case 表达式使用非常量

    t := time.Now()
    switch {
    case t.Hour() < 12:
        fmt.Println("it's before noon")
    default:
        fmt.Println("it's after noon")
    }
// _switch_ ,方便的条件分支语句。

package main

import "fmt"
import "time"

func main() {

    // 一个基本的 `switch`。
    i := 2
    fmt.Print("write ", i, " as ")
    switch i {
    case 1:
        fmt.Println("one")
    case 2:
        fmt.Println("two")
    case 3:
        fmt.Println("three")
    }

    // 在一个 `case` 语句中,你可以使用逗号来分隔多个表
    // 达式。在这个例子中,我们很好的使用了可选的
    // `default` 分支。
    switch time.Now().Weekday() {
    case time.Saturday, time.Sunday:
        fmt.Println("it's the weekend")
    default:
        fmt.Println("it's a weekday")
    }

    // 不带表达式的 `switch` 是实现 if/else 逻辑的另一种
    // 方式。这里展示了 `case` 表达式是如何使用非常量的。
    t := time.Now()
    switch {
    case t.Hour() < 12:
        fmt.Println("it's before noon")
    default:
        fmt.Println("it's after noon")
    }
}

// todo: type switches




输出:
write 2 as two
it's a weekday
it's after noon

数组

创建数组

创建了一个数组 a 来存放刚好 5 个 int元素的类型和长度都是数组类型的一部分数组默认是零值的,对于 int 数组来说也就是 0

 var a [5]int

注意红字这段话,后面和slice数据类型作比较

一行内初始化一个数组

b := [5]int{1, 2, 3, 4, 5}

创建二维数组并初始化

    var twoD [2][3]int
    for i := 0; i < 2; i++ {
        for j := 0; j < 3; j++ {
            twoD[i][j] = i + j
        }
    }
// 在 Go 中,_数组_ 是一个固定长度的数列。

package main

import "fmt"

func main() {

    // 这里我们创建了一个数组 `a` 来存放刚好 5 个 `int`。
    // 元素的类型和长度都是数组类型的一部分。数组默认是
    // 零值的,对于 `int` 数组来说也就是 `0`。
    var a [5]int
    fmt.Println("emp:", a)

    // 我们可以使用 `array[index] = value` 语法来设置数组
    // 指定位置的值,或者用 `array[index]` 得到值。
    a[4] = 100
    fmt.Println("set:", a)
    fmt.Println("get:", a[4])

    // 使用内置函数 `len` 返回数组的长度
    fmt.Println("len:", len(a))

    // 使用这个语法在一行内初始化一个数组
    b := [5]int{1, 2, 3, 4, 5}
    fmt.Println("dcl:", b)

    // 数组的存储类型是单一的,但是你可以组合这些数据
    // 来构造多维的数据结构。
    var twoD [2][3]int
    for i := 0; i < 2; i++ {
        for j := 0; j < 3; j++ {
            twoD[i][j] = i + j
        }
    }
    fmt.Println("2d: ", twoD)
}



输出:
emp: [0 0 0 0 0]
set: [0 0 0 0 100]
get: 100
len: 5
dcl: [1 2 3 4 5]
2d:  [[0 1 2] [1 2 3]]

切片

Slice数据类型

Slice 是 Go 中一个关键的数据类型,是一个比数组更加强大的序列接口,不像数组,slice 的类型仅由它所包含的元素决定(不像数组,数组类型由元素的类型和长度决定)

创建Slice

要创建一个长度非零的空slice,需要使用内建的方法 make。这里我们创建了一个长度为3的 string 类型 slice(初始化为零值)。

s := make([]string, 3)

Slice的append操作

作为基本操作的补充,slice 支持比数组更多的操作。其中一个是内建的 append,它返回一个包含了一个或者多个新值的 slice。注意我们接受返回由 append返回的新的 slice 值。

    s = append(s, "d")
    s = append(s, "e", "f")

Slice的“切片”操作

l := s[2:5]     //得到一个包含元素 s[2], s[3],s[4] 的 slice
l = s[:5]       //从 s[0] 到(但是不包含)s[5]的元素: s[0], s[1], s[2], s[3], s[4]
l = s[2:]      //得到从(包含)s[2] 到 slice 的后一个值的元素:s[2], s[3], s[4], s[5]

在一行代码中声明并初始化一个 slice 变量

t := []string{"g", "h", "i"}            //注意和声明数组的不同是不用声明数组长度

Slice组成多维数据结构

Slice 可以组成多维数据结构。内部的 slice 长度可以不同,这和多位数组不同。

    twoD := make([][]int, 3)
    for i := 0; i < 3; i++ {
        innerLen := i + 1
        twoD[i] = make([]int, innerLen)
        for j := 0; j < innerLen; j++ {
            twoD[i][j] = i + j
        }
    }
// _Slice_ 是 Go 中一个关键的数据类型,是一个比数组更
// 加强大的序列接口

package main

import "fmt"

func main() {

    // 不像数组,slice 的类型仅由它所包含的元素决定(不像
    // 数组中还需要元素的个数)。要创建一个长度非零的空
    // slice,需要使用内建的方法 `make`。这里我们创建了一
    // 个长度为3的 `string` 类型 slice(初始化为零值)。
    s := make([]string, 3)
    fmt.Println("emp:", s)

    // 我们可以和数组一样设置和得到值
    s[0] = "a"
    s[1] = "b"
    s[2] = "c"
    fmt.Println("set:", s)
    fmt.Println("get:", s[2])

    // 如你所料,`len` 返回 slice 的长度
    fmt.Println("len:", len(s))

    // 作为基本操作的补充,slice 支持比数组更多的操作。
    // 其中一个是内建的 `append`,它返回一个包含了一个
    // 或者多个新值的 slice。注意我们接受返回由 append
    // 返回的新的 slice 值。
    s = append(s, "d")
    s = append(s, "e", "f")
    fmt.Println("apd:", s)

    // Slice 也可以被 `copy`。这里我们创建一个空的和 `s` 有
    // 相同长度的 slice `c`,并且将 `s` 复制给 `c`。
    c := make([]string, len(s))
    copy(c, s)
    fmt.Println("cpy:", c)

    // Slice 支持通过 `slice[low:high]` 语法进行“切片”操
    // 作。例如,这里得到一个包含元素 `s[2]`, `s[3]`,
    // `s[4]` 的 slice。
    l := s[2:5]
    fmt.Println("sl1:", l)

    // 这个 slice 从 `s[0]` 到(但是包含)`s[5]`。
    l = s[:5]
    fmt.Println("sl2:", l)

    // 这个 slice 从(包含)`s[2]` 到 slice 的后一个值。
    l = s[2:]
    fmt.Println("sl3:", l)

    // 我们可以在一行代码中声明并初始化一个 slice 变量。
    t := []string{"g", "h", "i"}
    fmt.Println("dcl:", t)

    // Slice 可以组成多维数据结构。内部的 slice 长度可以不
    // 同,这和多位数组不同。
    twoD := make([][]int, 3)
    for i := 0; i < 3; i++ {
        innerLen := i + 1
        twoD[i] = make([]int, innerLen)
        for j := 0; j < innerLen; j++ {
            twoD[i][j] = i + j
        }
    }
    fmt.Println("2d: ", twoD)
}





输出:(注意,slice 和数组不同,虽然它们通过 fmt.Println 输出差不多。)
emp: [  ]
set: [a b c]
get: c
len: 3
apd: [a b c d e f]
cpy: [a b c d e f]
sl1: [c d e]
sl2: [a b c d e]
sl3: [c d e f]
dcl: [g h i]
2d:  [[0] [1 2] [2 3 4]]

关联数组(就是java中的map,只是另一个叫法)

创建空map

设置、打印、获取、删除、键值对

从map中取值获取到的两个返回值

在同一行申明和初始化一个新的map的语法

// _map_ 是 Go 内置[关联数据类型](http://zh.wikipedia.org/wiki/关联数组)(
// 在一些其他的语言中称为_哈希_ 或者_字典_ )。

package main

import "fmt"

func main() {

    // 要创建一个空 map,需要使用内建的 `make`:
    // `make(map[key-type]val-type)`.
    m := make(map[string]int)

    // 使用典型的 `make[key] = val` 语法来设置键值对。
    m["k1"] = 7
    m["k2"] = 13

    // 使用例如 `Println` 来打印一个 map 将会输出所有的
    // 键值对。
    fmt.Println("map:", m)

    // 使用 `name[key]` 来获取一个键的值
    v1 := m["k1"]
    fmt.Println("v1: ", v1)

    // 当对一个 map 调用内建的 `len` 时,返回的是键值对
    // 数目
    fmt.Println("len:", len(m))

    // 内建的 `delete` 可以从一个 map 中移除键值对
    delete(m, "k2")
    fmt.Println("map:", m)

    // 当从一个 map 中取值时,可选的第二返回值指示这个键
    // 是在这个 map 中。这可以用来消除键不存在和键有零值,
    // 像 `0` 或者 `""` 而产生的歧义。
    //此处不需要第一返回值,我们使用 空值定义符'_'来忽略它
    _, prs := m["k2"]
    fmt.Println("prs:", prs)

    // 你也可以通过这个语法在同一行申明和初始化一个新的
    // map。
    n := map[string]int{"foo": 1, "bar": 2}
    fmt.Println("map:", n)
}

Range 遍历

range迭代slice

range迭代map

range迭代字符串

// _range_ 迭代各种各样的数据结构。让我们来看看如何在我们
// 已经学过的数据结构上使用 `rang` 吧。

package main

import "fmt"

func main() {

    // 这里我们使用 `range` 来统计一个 slice 的元素个数。
    // 数组也可以采用这种方法。
    nums := []int{2, 3, 4}
    sum := 0
    for _, num := range nums {
        sum += num
    }
    fmt.Println("sum:", sum)

    // `range` 在数组和 slice 中都同样提供每个项的索引和
    // 值。上面我们不需要索引,所以我们使用 _空值定义符_
    // `_` 来忽略它。有时候我们实际上是需要这个索引的。
    for i, num := range nums {
        if num == 3 {
            fmt.Println("index:", i)
        }
    }

    // `range` 在 map 中迭代键值对。
    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }

    // `range` 在字符串中迭代 unicode 编码。第一个返回值是
    // `rune` 的起始字节位置,然后第二个是 `rune` 自己。
    for i, c := range "go" {
        fmt.Println(i, c)
    }
}




输出:
sum: 9
index: 1
b -> banana
a -> apple
0 103
1 111

函数

// _函数_ 是 Go 的中心。我们将通过一些不同的例子来
// 进行学习。

package main

import "fmt"

// 这里是一个函数,接受两个 `int` 并且以 `int` 返回它
// 们的和
func plus(a int, b int) int {

    // Go 需要明确的返回值,例如,它不会自动返回最
    // 后一个表达式的值
    return a + b
}

func main() {

    // 正如你期望的那样,通过 `name(args)` 来调用一
    // 个函数,
    res := plus(1, 2)
    fmt.Println("1+2 =", res)
}

// todo: coalesced parameter types


输出:
1+2 = 3

多返回值

如何定义多返回值函数

通过多赋值操作获取多个返回值

通过空白定义符'_'仅获取返回值的一部分

// Go 内建_多返回值_ 支持。这个特性在 Go 语言中经常被用到,
// 例如用来同时返回一个函数的结果和错误信息。

package main

import "fmt"

// `(int, int)` 在这个函数中标志着这个函数返回 2 个 `int`。
func vals() (int, int) {
    return 3, 7
}

func main() {

    // 这里我们通过_多赋值_ 操作来使用这两个不同的返回值。
    a, b := vals()
    fmt.Println(a)
    fmt.Println(b)

    // 如果你仅仅想返回值的一部分的话,你可以使用空白定
    // 义符 `_`。
    _, c := vals()
    fmt.Println(c)
}

// todo: named return parameters
// todo: naked returns



输出:
3
7
7

变参函数

常见的变参函数:fmt.Println

如何将slice作为变参函数调用

// [_可变参数函数_](http://zh.wikipedia.org/wiki/可變參數函數)。可以用任意
// 数量的参数调用。例如,`fmt.Println` 是一个常见的变参函数。

package main

import "fmt"

// 这个函数使用任意数目的 `int` 作为参数。
func sum(nums ...int) {
    fmt.Print(nums, " ")
    total := 0
    for _, num := range nums {
        total += num
    }
    fmt.Println(total)
}

func main() {

    // 变参函数使用常规的调用方式,除了参数比较特殊。
    sum(1, 2)
    sum(1, 2, 3)

    // 如果你的 slice 已经有了多个值,想把它们作为变参
    // 使用,你要这样调用 `func(slice...)`。
    nums := []int{1, 2, 3, 4}
    sum(nums...)
}




输出:
[1 2] 3
[1 2 3] 6
[1 2 3 4] 10

闭包

闭包就是通过函数返回函数,这个返回的函数可以使用闭包的方式隐藏变量

// Go 支持通过 <a href="http://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)"><em>闭包</em></a>
// 来使用 [_匿名函数_](http://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)。
// 匿名函数在你想定义一个不需要命名的内联函数时是很实用的。

package main

import "fmt"

// 这个 `intSeq` 函数返回另一个在 `intSeq` 函数体内定义的
// 匿名函数。这个返回的函数使用闭包的方式 _隐藏_ 变量 `i`。
func intSeq() func() int {
    i := 0
    return func() int {
        i += 1
        return i
    }
}

func main() {

    // 我们调用 `intSeq` 函数,将返回值(也是一个函数)赋给
    // `nextInt`。这个函数的值包含了自己的值 `i`,这样在每
    // 次调用 `nextInt` 时都会更新 `i` 的值。
    nextInt := intSeq()

    // 通过多次调用 `nextInt` 来看看闭包的效果。
    fmt.Println(nextInt())
    fmt.Println(nextInt())
    fmt.Println(nextInt())

    // 为了确认这个状态对于这个特定的函数是唯一的,我们
    // 重新创建并测试一下。
    newInts := intSeq()
    fmt.Println(newInts())
}





输出:
1
2
3
1

递归(和其他语言一样)

// Go 支持 <a href="http://zh.wikipedia.org/wiki/%E9%80%92%E5%BD%92_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)"><em>递归</em></a>。
// 这里是一个经典的阶乘示例。

package main

import "fmt"

// `face` 函数在到达 `face(0)` 前一直调用自身。
func fact(n int) int {
    if n == 0 {
        return 1
    }
    return n * fact(n-1)
}

func main() {
    fmt.Println(fact(7))
}



输出:
5040

指针(和C一样,没啥新变化)

// Go 支持 <em><a href="http://zh.wikipedia.org/wiki/%E6%8C%87%E6%A8%99_(%E9%9B%BB%E8%85%A6%E7%A7%91%E5%AD%B8)">指针</a></em>,
// 允许在程序中通过引用传递值或者数据结构。

package main

import "fmt"

// 我们将通过两个函数:`zeroval` 和 `zeroptr` 来比较指针和
// 值类型的不同。`zeroval` 有一个 `int` 型参数,所以使用值
// 传递。`zeroval` 将从调用它的那个函数中得到一个 `ival`
// 形参的拷贝。
func zeroval(ival int) {
    ival = 0
}

// `zeroptr` 有一和上面不同的 `*int` 参数,意味着它用了一
// 个 `int`指针。函数体内的 `*iptr` 接着_解引用_ 这个指针,
// 从它内存地址得到这个地址对应的当前值。对一个解引用的指
// 针赋值将会改变这个指针引用的真实地址的值。
func zeroptr(iptr *int) {
    *iptr = 0
}

func main() {
    i := 1
    fmt.Println("initial:", i)

    zeroval(i)
    fmt.Println("zeroval:", i)

    // 通过 `&i` 语法来取得 `i` 的内存地址,例如一个变量
    // `i` 的指针。
    zeroptr(&i)
    fmt.Println("zeroptr:", i)

    // 指针也是可以被打印的。
    fmt.Println("pointer:", &i)
}


输出:
initial: 1
zeroval: 1
zeroptr: 0
pointer: 0xc000018050

结构体

// Go 的_结构体_ 是各个字段字段的类型的集合。
// 这在组织数据时非常有用。

package main

import "fmt"

// 这里的 `person` 结构体包含了 `name` 和 `age` 两个字段。
type person struct {
    name string
    age  int
}

func main() {

    // 使用这个语法创建了一个新的结构体元素。
    fmt.Println(person{"Bob", 20})

    // 你可以在初始化一个结构体元素时指定字段名字。
    fmt.Println(person{name: "Alice", age: 30})

    // 省略的字段将被初始化为零值。
    fmt.Println(person{name: "Fred"})

    // `&` 前缀生成一个结构体指针。
    fmt.Println(&person{name: "Ann", age: 40})

    // 使用点来访问结构体字段。
    s := person{name: "Sean", age: 50}
    fmt.Println(s.name)

    // 也可以对结构体指针使用`.` - 指针会被自动解引用。
    sp := &s
    fmt.Println(sp.age)

    // 结构体是可变的。
    sp.age = 51
    fmt.Println(sp.age)
}




输出:
{Bob 20}
{Alice 30}
{Fred 0}
&{Ann 40}
Sean
50
51

在结构体类型中定义方法 

// Go 支持在结构体类型中定义_方法_ 。

package main

import "fmt"

type rect struct {
    width, height int
}

// 这里的 `area` 方法有一个_接收器类型_ `rect`。
func (r *rect) area() int {
    return r.width * r.height
}

// 可以为值类型或者指针类型的接收器定义方法。这里是一个
// 值类型接收器的例子。
func (r rect) perim() int {
    return 2*r.width + 2*r.height
}

func main() {
    r := rect{width: 10, height: 5}

    // 这里我们调用上面为结构体定义的两个方法。
    fmt.Println("area: ", r.area())
    fmt.Println("perim:", r.perim())

    // Go 自动处理方法调用时的值和指针之间的转化。你可以使
    // 用指针来调用方法来避免在方法调用时产生一个拷贝,或者
    // 让方法能够改变接受的数据。
    rp := &r
    fmt.Println("area: ", rp.area())
    fmt.Println("perim:", rp.perim())
}



输出:
area:  50
perim: 30
area:  50
perim: 30

接口

// _接口_ 是方法特征的命名集合。

package main

import "fmt"
import "math"

// 这里是一个几何体的基本接口。
type geometry interface {
    area() float64
    perim() float64
}

// 在我们的例子中,我们将让 `rect` 和 `circle` 实现
// 这个接口
type rect struct {
    width, height float64
}
type circle struct {
    radius float64
}

// 要在 Go 中实现一个接口,我们只需要实现接口中的所有
// 方法。这里我们让 `rect` 实现了 `geometry` 接口。
func (r rect) area() float64 {
    return r.width * r.height
}
func (r rect) perim() float64 {
    return 2*r.width + 2*r.height
}

// `circle` 的实现。
func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
    return 2 * math.Pi * c.radius
}

// 如果一个变量的是接口类型,那么我们可以调用这个被命名的
// 接口中的方法。这里有一个一通用的 `measure` 函数,利用这个
// 特性,它可以用在任何 `geometry` 上。
func measure(g geometry) {
    fmt.Println(g)
    fmt.Println(g.area())
    fmt.Println(g.perim())
}

func main() {
    r := rect{width: 3, height: 4}
    c := circle{radius: 5}

    // 结构体类型 `circle` 和 `rect` 都实现了 `geometry`
    // 接口,所以我们可以使用它们的实例作为 `measure` 的参数。
    measure(r)
    measure(c)
}



输出:
{3 4}
12
14
{5}
78.53981633974483
31.41592653589793

排序函数的使用

// Go 的 `sort` 包实现了内置和用户自定义数据类型的排序
// 功能。我们首先关注内置数据类型的排序。

package main

import "fmt"
import "sort"

func main() {

    // 排序方法是正对内置数据类型的;这里是一个字符串的例子。
    // 注意排序是原地更新的,所以他会改变给定的序列并且不返回
    // 一个新值。
    strs := []string{"c", "a", "b"}
    sort.Strings(strs)
    fmt.Println("Strings:", strs)

    // 一个 `int` 排序的例子。
    ints := []int{7, 2, 4}
    sort.Ints(ints)
    fmt.Println("Ints:   ", ints)

    // 我们也可以使用 `sort` 来检查一个序列是不是已经
    // 是排好序的。
    s := sort.IntsAreSorted(ints)
    fmt.Println("Sorted: ", s)
}




运行程序,打印排序好的字符串和整形序列以及我们 AreSorted测试的结构 true。
Strings: [a b c]
Ints:    [2 4 7]
Sorted:  true

使用函数自定义排序

// 有时候我们想使用和集合的自然排序不同的方法对集合进行排序。
// 例如,我们想按照字母的长度而不是首字母顺序对字符串排序。
// 这里是一个 Go 自定义排序的例子。

package main

import "sort"
import "fmt"

// 为了在 Go 中使用自定义函数进行排序,我们需要一个对应的
// 类型。这里我们创建一个为内置 `[]string` 类型的别名的
// `ByLength` 类型,
type ByLength []string

// 我们在类型中实现了 `sort.Interface` 的 `Len`,`Less`
// 和 `Swap` 方法,这样我们就可以使用 `sort` 包的通用
// `Sort` 方法了,`Len` 和 `Swap` 通常在各个类型中都差
// 不多,`Less` 将控制实际的自定义排序逻辑。在我们的例
// 子中,我们想按字符串长度增加的顺序来排序,所以这里
// 使用了 `len(s[i])` 和 `len(s[j])`。
func (s ByLength) Len() int {
    return len(s)
}
func (s ByLength) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}
func (s ByLength) Less(i, j int) bool {
    return len(s[i]) < len(s[j])
}

// 一切都准备好了,我们现在可以通过将原始的 `fruits` 切片转型成 `ByLength` 来实现我们的自定排序了。然后对这个转型的切片使用 `sort.Sort` 方法。
func main() {
    fruits := []string{"peach", "banana", "kiwi"}
    //这里"ByLength(fruits)"猜测是一个类型转换,将[]string类型转换为ByLength类型
    sort.Sort(ByLength(fruits))
    fmt.Println(fruits)
}





输出:
[kiwi peach banana]

组合函数(将函数作为参数传递并使用,类似C的函数指针)

// 我们经常需要程序在数据集上执行操作,比如选择满足给定条件
// 的所有项,或者将所有的项通过一个自定义函数映射到一个新的
// 集合上。

// 在某些语言中,会习惯使用[泛型](http://zh.wikipedia.org/wiki/%E6%B3%9B%E5%9E%8B)。
// Go 不支持泛型,在 Go 中,当你的程序或者数据类型需要
// 时,通常是通过组合的方式来提供操作函数。

// 这是一些 `strings` 切片的组合函数示例。你可以使用这些例
// 子来构建自己的函数。注意有时候,直接使用内联组合操作代
// 码会更清晰,而不是创建并调用一个帮助函数。

package main

import "strings"
import "fmt"

// 返回目标字符串 `t` 出现的的第一个索引位置,或者在没有匹
// 配值时返回 -1。
func Index(vs []string, t string) int {
    for i, v := range vs {
        if v == t {
            return i
        }
    }
    return -1
}

// 如果目标字符串 `t` 在这个切片中则返回 `true`。
func Include(vs []string, t string) bool {
    return Index(vs, t) >= 0
}

// 如果这些切片中的字符串有一个满足条件 `f` 则返回
// `true`。
func Any(vs []string, f func(string) bool) bool {
    for _, v := range vs {
        if f(v) {
            return true
        }
    }
    return false
}

// 如果切片中的所有字符串都满足条件 `f` 则返回 `true`。
func All(vs []string, f func(string) bool) bool {
    for _, v := range vs {
        if !f(v) {
            return false
        }
    }
    return true
}

// 返回一个包含所有切片中满足条件 `f` 的字符串的新切片。
func Filter(vs []string, f func(string) bool) []string {
    vsf := make([]string, 0)
    for _, v := range vs {
        if f(v) {
            vsf = append(vsf, v)
        }
    }
    return vsf
}

// 返回一个对原始切片中所有字符串执行函数 `f` 后的新切片。
func Map(vs []string, f func(string) string) []string {
    vsm := make([]string, len(vs))
    for i, v := range vs {
        vsm[i] = f(v)
    }
    return vsm
}

func main() {

    // 这里试试这些组合函数。
    var strs = []string{"peach", "apple", "pear", "plum"}

    fmt.Println(Index(strs, "pear"))

    fmt.Println(Include(strs, "grape"))

    fmt.Println(Any(strs, func(v string) bool {
        return strings.HasPrefix(v, "p")
    }))

    fmt.Println(All(strs, func(v string) bool {
        return strings.HasPrefix(v, "p")
    }))

    fmt.Println(Filter(strs, func(v string) bool {
        return strings.Contains(v, "e")
    }))

    // 上面的例子都是用的匿名函数,但是你也可以使用类型正确
    // 的命名函数
    fmt.Println(Map(strs, strings.ToUpper))

}





输出:
2
false
true
false
[peach apple pear]
[PEACH APPLE PEAR PLUM]

字符串函数

// 标准库的 `strings` 包提供了很多有用的字符串相关的函数。
// 这里是一些用来让你对这个包有个初步了解的例子。

package main

import s "strings"
import "fmt"

// 我们给 `fmt.Println` 一个短名字的别名,我们随后将会经常
// 用到。
var p = fmt.Println

func main() {

    // 这是一些 `strings` 中的函数例子。注意他们都是包中的
    // 函数,不是字符串对象自身的方法,这意味着我们需要考
    // 虑在调用时传递字符作为第一个参数进行传递。
    p("Contains:  ", s.Contains("test", "es"))
    p("Count:     ", s.Count("test", "t"))
    p("HasPrefix: ", s.HasPrefix("test", "te"))
    p("HasSuffix: ", s.HasSuffix("test", "st"))
    p("Index:     ", s.Index("test", "e"))
    p("Join:      ", s.Join([]string{"a", "b"}, "-"))
    p("Repeat:    ", s.Repeat("a", 5))
    p("Replace:   ", s.Replace("foo", "o", "0", -1))
    p("Replace:   ", s.Replace("foo", "o", "0", 1))
    p("Split:     ", s.Split("a-b-c-d-e", "-"))
    p("ToLower:   ", s.ToLower("TEST"))
    p("ToUpper:   ", s.ToUpper("test"))
    p()

    // 你可以在 [`strings`](http://golang.org/pkg/strings/)
    // 包文档中找到更多的函数

    // 虽然不是 `strings` 的一部分,但是仍然值得一提的是获
    // 取字符串长度和通过索引获取一个字符的机制。
    p("Len: ", len("hello"))
    p("Char:", "hello"[1])
}





输出:
Contains:   true
Count:      2
HasPrefix:  true
HasSuffix:  true
Index:      1
Join:       a-b
Repeat:     aaaaa
Replace:    f00
Replace:    f0o
Split:      [a b c d e]
ToLower:    test
ToUpper:    TEST

Len:  5
Char: 101

Printf格式化输出

// Go 在传统的`printf` 中对字符串格式化提供了优异的支持。
// 这里是一些基本的字符串格式化的人物的例子。

package main

import "fmt"
import "os"

type point struct {
    x, y int
}

func main() {

    // Go 为常规 Go 值的格式化设计提供了多种打印方式。例
    // 如,这里打印了 `point` 结构体的一个实例。
    p := point{1, 2}
    fmt.Printf("%v\n", p)

    // 如果值是一个结构体,`%+v` 的格式化输出内容将包括
    // 结构体的字段名。
    fmt.Printf("%+v\n", p)

    // `%#v` 形式则输出这个值的 Go 语法表示。例如,值的
    // 运行源代码片段。
    fmt.Printf("%#v\n", p)

    // 需要打印值的类型,使用 `%T`。
    fmt.Printf("%T\n", p)

    // 格式化布尔值是简单的。
    fmt.Printf("%t\n", true)

    // 格式化整形数有多种方式,使用 `%d`进行标准的十进
    // 制格式化。
    fmt.Printf("%d\n", 123)

    // 这个输出二进制表示形式。
    fmt.Printf("%b\n", 14)

    // 这个输出给定整数的对应字符。
    fmt.Printf("%c\n", 33)

    // `%x` 提供十六进制编码。
    fmt.Printf("%x\n", 456)

    // 对于浮点型同样有很多的格式化选项。使用 `%f` 进
    // 行最基本的十进制格式化。
    fmt.Printf("%f\n", 78.9)

    // `%e` 和 `%E` 将浮点型格式化为(稍微有一点不
    // 同的)科学技科学记数法表示形式。
    fmt.Printf("%e\n", 123400000.0)
    fmt.Printf("%E\n", 123400000.0)

    // 使用 `%s` 进行基本的字符串输出。
    fmt.Printf("%s\n", "\"string\"")

    // 像 Go 源代码中那样带有双引号的输出,使用 `%q`。
    fmt.Printf("%q\n", "\"string\"")

    // 和上面的整形数一样,`%x` 输出使用 base-16 编码的字
    // 符串,每个字节使用 2 个字符表示。
    fmt.Printf("%x\n", "hex this")

    // 要输出一个指针的值,使用 `%p`。
    fmt.Printf("%p\n", &p)

    // 当输出数字的时候,你将经常想要控制输出结果的宽度和
    // 精度,可以使用在 `%` 后面使用数字来控制输出宽度。
    // 默认结果使用右对齐并且通过空格来填充空白部分。
    fmt.Printf("|%6d|%6d|\n", 12, 345)

    // 你也可以指定浮点型的输出宽度,同时也可以通过 宽度.
    // 精度 的语法来指定输出的精度。
    fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)

    // 要左对齐,使用 `-` 标志。
    fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)

    // 你也许也想控制字符串输出时的宽度,特别是要确保他们在
    // 类表格输出时的对齐。这是基本的右对齐宽度表示。
    fmt.Printf("|%6s|%6s|\n", "foo", "b")

    // 要左对齐,和数字一样,使用 `-` 标志。
    fmt.Printf("|%-6s|%-6s|\n", "foo", "b")

    // 到目前为止,我们已经看过 `Printf`了,它通过 `os.Stdout`
    // 输出格式化的字符串。`Sprintf` 则格式化并返回一个字
    // 符串而不带任何输出。
    s := fmt.Sprintf("a %s", "string")
    fmt.Println(s)

    // 你可以使用 `Fprintf` 来格式化并输出到 `io.Writers`
    // 而不是 `os.Stdout`。
    fmt.Fprintf(os.Stderr, "an %s\n", "error")
}



输出:
{1 2}
{x:1 y:2}
main.point{x:1, y:2}
main.point
true
123
1110
!
1c8
78.900000
1.234000e+08
1.234000E+08
"string"
"\"string\""
6865782074686973
0xc0000b6020
|    12|   345|
|  1.20|  3.45|
|1.20  |3.45  |
|   foo|     b|
|foo   |b     |
a string
an error

正则表达式

// Go 在传统的`printf` 中对字符串格式化提供了优异的支持。
// 这里是一些基本的字符串格式化的人物的例子。

package main

import "fmt"
import "os"

type point struct {
    x, y int
}

func main() {

    // Go 为常规 Go 值的格式化设计提供了多种打印方式。例
    // 如,这里打印了 `point` 结构体的一个实例。
    p := point{1, 2}
    fmt.Printf("%v\n", p)

    // 如果值是一个结构体,`%+v` 的格式化输出内容将包括
    // 结构体的字段名。
    fmt.Printf("%+v\n", p)

    // `%#v` 形式则输出这个值的 Go 语法表示。例如,值的
    // 运行源代码片段。
    fmt.Printf("%#v\n", p)

    // 需要打印值的类型,使用 `%T`。
    fmt.Printf("%T\n", p)

    // 格式化布尔值是简单的。
    fmt.Printf("%t\n", true)

    // 格式化整形数有多种方式,使用 `%d`进行标准的十进
    // 制格式化。
    fmt.Printf("%d\n", 123)

    // 这个输出二进制表示形式。
    fmt.Printf("%b\n", 14)

    // 这个输出给定整数的对应字符。
    fmt.Printf("%c\n", 33)

    // `%x` 提供十六进制编码。
    fmt.Printf("%x\n", 456)

    // 对于浮点型同样有很多的格式化选项。使用 `%f` 进
    // 行最基本的十进制格式化。
    fmt.Printf("%f\n", 78.9)

    // `%e` 和 `%E` 将浮点型格式化为(稍微有一点不
    // 同的)科学技科学记数法表示形式。
    fmt.Printf("%e\n", 123400000.0)
    fmt.Printf("%E\n", 123400000.0)

    // 使用 `%s` 进行基本的字符串输出。
    fmt.Printf("%s\n", "\"string\"")

    // 像 Go 源代码中那样带有双引号的输出,使用 `%q`。
    fmt.Printf("%q\n", "\"string\"")

    // 和上面的整形数一样,`%x` 输出使用 base-16 编码的字
    // 符串,每个字节使用 2 个字符表示。
    fmt.Printf("%x\n", "hex this")

    // 要输出一个指针的值,使用 `%p`。
    fmt.Printf("%p\n", &p)

    // 当输出数字的时候,你将经常想要控制输出结果的宽度和
    // 精度,可以使用在 `%` 后面使用数字来控制输出宽度。
    // 默认结果使用右对齐并且通过空格来填充空白部分。
    fmt.Printf("|%6d|%6d|\n", 12, 345)

    // 你也可以指定浮点型的输出宽度,同时也可以通过 宽度.
    // 精度 的语法来指定输出的精度。
    fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)

    // 要左对齐,使用 `-` 标志。
    fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)

    // 你也许也想控制字符串输出时的宽度,特别是要确保他们在
    // 类表格输出时的对齐。这是基本的右对齐宽度表示。
    fmt.Printf("|%6s|%6s|\n", "foo", "b")

    // 要左对齐,和数字一样,使用 `-` 标志。
    fmt.Printf("|%-6s|%-6s|\n", "foo", "b")

    // 到目前为止,我们已经看过 `Printf`了,它通过 `os.Stdout`
    // 输出格式化的字符串。`Sprintf` 则格式化并返回一个字
    // 符串而不带任何输出。
    s := fmt.Sprintf("a %s", "string")
    fmt.Println(s)

    // 你可以使用 `Fprintf` 来格式化并输出到 `io.Writers`
    // 而不是 `os.Stdout`。
    fmt.Fprintf(os.Stderr, "an %s\n", "error")
}



输出:
{1 2}
{x:1 y:2}
main.point{x:1, y:2}
main.point
true
123
1110
!
1c8
78.900000
1.234000e+08
1.234000E+08
"string"
"\"string\""
6865782074686973
0xc0000b6020
|    12|   345|
|  1.20|  3.45|
|1.20  |3.45  |
|   foo|     b|
|foo   |b     |
a string
an error(红色字体)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值