通过例子学习golang语言基础(一)

Go 编程实例 Go by Example

原文作者:Go 技术论坛文档:《Go 编程实例 Go by Example(2020)》
转自链接:https://learnku.com/docs/gobyexample/2020

1.HelloWorld

package main

import "fmt"

func main() {
	fmt.Println("hello world")
}

2.datatypes

package main

import "fmt"

func main() {
	//字符串通过+号拼接
	fmt.Println("hello"+"world")
	// 整数和浮点数
	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)
}

3.variables

package main

import "fmt"

func main() {

	// `var` 声明 1 个或者多个变量。
	var a = "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)
}

4.const

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))
}

5.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
    }

    // 你也可以使用 `continue` 来跳到下一个循环迭代
    for n := 0; n <= 5; n++ {
        if n%2 == 0 {
            continue
        }
        fmt.Println(n)
    }
}

6.if_else

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")
	}
}

7.switch_case

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")
	}

	// 类型开关 (`type switch`) 比较类型而非值。可以用来发现一个接口值的
	// 类型。在这个例子中,变量 `t` 在每个分支中会有相应的类型。
	whatAmI := func(i interface{}) {
		switch t := i.(type) {
		case bool:
			fmt.Println("I'm a bool")
		case int:
			fmt.Println("I'm an int")
		default:
			fmt.Printf("Don't know type %T\n", t)
		}
	}
	whatAmI(true)
	whatAmI(1)
	whatAmI("hey")
}

8.array

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)
}

9.slice

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]` (包含)开始切片。
	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)
}

10.map

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

	// 使用例如 `fmt.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` 或者 `""` 而产生的歧义。这里我们不需要值,所以
	// 用_空白标识符(blank identifier)_忽略。
	_, prs := m["k2"]
	fmt.Println("prs:", prs)

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

11.range

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` 也可以只遍历 map 的键。
	for k := range kvs {
		fmt.Println("key:", k)
	}

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

12.function

package main

import "fmt"

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

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

// 当多个连续的参数为同样类型时,最多可以仅声明最后一个参数类型
// 而忽略之前相同类型参数的类型声明。
func plusPlus(a, b, c int) int {
	return a + b + c
}

func main() {

	// 通过 `name(args)` 来调用函数,
	res := plus(1, 2)
	fmt.Println("1+2 =", res)

	res = plusPlus(1, 2, 3)
	fmt.Println("1+2+3 =", res)
}

13.多返回值func

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)
}

14.变参func

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...)
}

15.闭包closures

package main

import "fmt"

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

16.递归recursion

package main

import "fmt"

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

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

17.pointer

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)
}

18.struct

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)

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

19.method

package main

import "fmt"

type rect struct {
	width, height int
}

// 这里的 `area` 方法有一个_接收器(receiver)类型_ `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())
}

20.interface

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)
}

21.error

package main

import "errors"
import "fmt"

// 按照惯例,错误通常是最后一个返回值并且是 `error` 类
// 型,一个内建的接口。
func f1(arg int) (int, error) {
	if arg == 42 {

		// `errors.New` 构造一个使用给定的错误信息的基本
		// `error` 值。
		return -1, errors.New("can't work with 42")

	}

	// 返回错误值为 nil 代表没有错误。
	return arg + 3, nil
}

// 通过实现 `Error` 方法来自定义 `error` 类型是可以的。
// 这里使用自定义错误类型来表示上面例子中的参数错误。
type argError struct {
	arg  int
	prob string
}

func (e *argError) Error() string {
	return fmt.Sprintf("%d - %s", e.arg, e.prob)
}

func f2(arg int) (int, error) {
	if arg == 42 {

		// 在这个例子中,我们使用 `&argError` 语法来建立一个
		// 新的结构体,并提供了 `arg` 和 `prob` 这两个字段
		// 的值。
		return -1, &argError{arg, "can't work with it"}
	}
	return arg + 3, nil
}

func main() {

	// 下面的两个循环测试了各个返回错误的函数。注意在 `if`
	// 行内的错误检查代码,在 Go 中是一个普遍的用法。
	for _, i := range []int{7, 42} {
		if r, e := f1(i); e != nil {
			fmt.Println("f1 failed:", e)
		} else {
			fmt.Println("f1 worked:", r)
		}
	}
	for _, i := range []int{7, 42} {
		if r, e := f2(i); e != nil {
			fmt.Println("f2 failed:", e)
		} else {
			fmt.Println("f2 worked:", r)
		}
	}

	// 你如果想在程序中使用一个自定义错误类型中的数据,你
	// 需要通过类型断言来得到这个错误类型的实例。
	_, e := f2(42)
	if ae, ok := e.(*argError); ok {
		fmt.Println(ae.arg)
		fmt.Println(ae.prob)
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值