Go 语言之旅_1

切片 slices

练习:实现 Pic。它应当返回一个长度为 dy 的切片,其中每个元素是一个长度为 dx,元素类型为 uint8 的切片。当你运行此程序时,它会将每个整数解释为灰度值(好吧,其实是蓝度值)并显示它所对应的图像。https://tour.go-zh.org/moretypes/18

  • 其实就是返回一个二维的切片,就和二维数组差不多意思。当时沙雕了,不记得切片要用make()来申请大小,还一直在括号里面写。然后就一直报错,数组越界。后来后面有个地方重新要用到这个例子的时候才想起来。
package main

import "golang.org/x/tour/pic"

func Pic(dx, dy int) [][]uint8 {
	var pic = make([][]uint8, dy)//先申请行
	for y := 0; y < dy; y ++{
		for x := 0; x < dx; x ++ {
			pic[y] = make([]uint8, dx)//在申请列
			pic[y][x] = uint8(x * y)
		}
	}
	return pic
}

func main() {
	pic.Show(Pic)
} 

Map

练习:实现 WordCount。它应当返回一个映射,其中包含字符串 s 中每个“单词”的个数。函数 wc.Test 会对此函数执行一系列测试用例,并输出成功还是失败。https://tour.go-zh.org/moretypes/23

  • 这里就是统计英语句子里的单词个数,然后它提示了一个很好用的方法strings.Fields。他直接将句子的空格去掉,把单词拿出来,变成一个字符串数组返回出来,然后直接range遍历一遍就是。
package main

import (
	"golang.org/x/tour/wc"
	"strings"

)

func WordCount(s string) map[string]int {
	var wc = make(map[string]int)
	for _,v := range strings.Fields(s) {
		if _, ok := wc[v]; ok == true {
			wc[v]++
		} else {
			wc[v] = 1
		}
	}
	return wc
}

func main() {
	wc.Test(WordCount)
	
}

函数闭包

1.函数值 function-values

  • 函数也可以事值。可以像其他值一样传递,可以作为函数的参数或者返回值。感觉这个地方有点C++里面函数指针的感觉。
package main

import (
	"fmt"
	"math"
)

func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

func main() {
	//hypot等于一个参数是两个浮点数下x,y,返回值也是浮点数的函数,函数是求x,y的平方和的平方根
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	
	//所以给hypot()传5,12得出13
	fmt.Println(hypot(5, 12))
	
	//compute的第一个参数是一个参数是两个浮点数,返回值是浮点数的函数fn,本身的返回值是fn的返回值,及浮点数。return的时候fn里的参数是3,4
	//当下面语句执行时,compute调用,fn等于hypot,在调用hypot等于的函数,并且参数是3,4,结果为5
	fmt.Println(compute(hypot))
	
	//这次fn等于Pow,求x的y次方,3的4次方结果81
	fmt.Println(compute(math.Pow))
}

2.函数的闭包 function-closure

  • Go 函数可以是一个闭包。闭包是一个函数值,它引用了其函数体之外的变量。该函数可以访问并赋予其引用的变量的值,换句话说,该函数被这些变量“绑定”在一起。
  • 他的解释是函数 adder 返回一个闭包。每个闭包都被绑定在其各自的 sum 变量上。
  • 这里感觉很难懂,搞托不清。?还是直接看例子。首先adder()的返回值就是一个函数值,所以执行pos,neg := adder(), adder()之后pos, neg还是一个函数,但是前面的sum已经申明过一次了,就存在在那里了,后面每一次的pos(i)就是调用一次里面闭包,sum就把每次的x累加起来了。
package main

import "fmt"

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x	
		return sum
	}
}

func main() {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(-2*i),
		)
	}
}

输出结果是:

0 0
1 -2
3 -6
6 -12
10 -20
15 -30
21 -42
28 -56
36 -72
45 -90

3.斐波拉契闭包 fibonacci-closure

练习:实现一个 fibonacci 函数,它返回一个函数(闭包),该闭包返回一个斐波纳契数列 (0, 1, 1, 2, 3, 5, ...)

  • 一开始没搞懂,还是看了别人了。? f := fibonacci()后留下f1,f2,然后f()重复10次,相当于重复f1, f2 = f2, (f1 + f2)10次。
package main

import "fmt"

// 返回一个“返回int的函数”
func fibonacci() func() int {
	f1,f2 := 0, 1
	return func() int {
		f1, f2 = f2, (f1 + f2)
		return f2 - f1
	}
}

func main() {
	f := fibonacci()
	for i := 0; i < 10; i++ {
		fmt.Println(f())
	}
}

Go里面还有几个方法可以实现Fibonacci,用通道的比较特别,到时候加上。

接口 Interface

推荐看看这个帖子链接。

1.接口定义、实现

  • 接口类型 是由一组方法签名定义的集合。这里其实还没有感觉出有什么作用,因为不用这个接口一样也可以实现这个功能,除了后面的实现的时候不用先搞个接口值,等于某个实现了接口的类型后,在调用接口。
package main

import "fmt"

type I interface {
	M()
}

type T struct {
	S string
}

// 此方法表示类型 T 实现了接口 I,但我们无需显式声明此事。
func (t T) M() {
	fmt.Println(t.S)
}

func main() {
	var i I = T{"hello"}
	i.M()//输出hello
}

2.接口值 interface-values

接口也是值。它们可以像其它值一样传递。接口值可以用作函数的参数或返回值。
在内部,接口值可以看做包含值和具体类型的元组:
(value, type)
接口值保存了一个具体底层类型的具体值。接口值调用方法时会执行其底层类型的同名方法。

package main

import (
	"fmt"
	"math"
)

type I interface {
	M()
}

type T struct {
	S string
}

func (t *T) M() {
	if t == nil {
		fmt.Println("")
		return
	}
	fmt.Println(t.S)
}

type F float64

func (f F) M() {
	fmt.Println(f)
}

func main() {
	var i I

	i = &T{"Hello"}//T的M()是在T的指针类型下实现的,所以要加&,不然会报错
	describe(i)
	i.M()

	i = F(math.Pi)//i 是一个接口值,可以等于任意类型,所以之前等于结构体T,这里又可以等于F
	describe(i)
	i.M()
	//底层值为nil的接口值
	var t *T//t是nil
	i = t
	describe(i)//输出的时候%v是,但是类型并不是nil
	i.M()
	
	var i1 I//nil接口值
	describe(i1)//输出两个nil
	i1.M()//这里就该报panic了,这里的接口值本身就是nil,既不保存值也不保存具体类型
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}

输出结果:

(&{Hello}, *main.T)
Hello
(3.141592653589793, main.F)
3.141592653589793
(<nil>, *main.T)
<nil>
(<nil>, <nil>)
panic: runtime error: invalid memory address or nil pointer dereference

3.空接口 empty-interface

指定了零个方法的接口值被称为 空接口
interface{}
空接口可保存任何类型的值。(因为每个类型都至少实现了零个方法。)空接口被用来处理未知类型的值。

  • 一开始没怎么搞懂这东西意思,后来看到师兄给的代码里面有个东西和这个很像。
    要打印字符串直接用Log(str)。但是这三个…不懂什么意思,好像是go的语法糖?。留个坑
func Log(v ...interface{}) {
	fmt.Println(v...)
}

4.类型断言 type-assertions

类型断言 提供了访问接口值底层具体值的方式。

  • 这里之前也在师兄代码里面看见过,不知道什么意思,看到这个才知道。就是可以判断这个接口值的具体类型。可以用单个判断的格式:
    t, ok := i.(T) 这个写法和判断Map是否存在一个元素差不多v, ok := map[k]
  • 也可以用switch实现类型选择
switch v := i.(type) {
case T:
    // v 的类型为 T
case S:
    // v 的类型为 S
default:
    // 没有匹配,v 与 i 的类型相同
}

先记录这么多,还有很多没有明白的地方。

你可能感兴趣的:(Go)