Go学习笔记数组和切片部分

数组的声明

Go 语言中的数组是一种 值类型(不像 C/C++ 中是指向首元素的指针)
所以可以通过 new() 来创建: var arr1 = new([5]int)。
正常的创建方法是这样的 var arr2 [5]int
那么这两种方法的区别是什么呢?arr1 的类型是 *[5]int,而 arr2 的类型是 [5]int。

练习7.1:array_value.go:

证明当数组赋值时,发生了数组内存拷贝。

package main

import "fmt"

func chang(ss [5]int) {
	ss[0] = 89
	fmt.Print(ss, "\n")
}
func chang1(ss *[5]int) {
	ss[0] = 89
	fmt.Print(*ss, "\n")
}
func main() {
	a := [5]int{1, 2, 3, 4, 5}
	fmt.Print(a, "\n")
	chang(a)
	fmt.Print(a, "\n")
	//指针形式
	chang1(&a)
	fmt.Print(a, "\n")
}

结果:
在这里插入图片描述

练习7.2:for_array.go:

写一个循环并用下标给数组赋值(从 0 到 15)并且将数组打印在屏幕上。

package main

import "fmt"

func main() {
	var a [15]int
	fmt.Print(a)
	for i, _ := range a {
		a[i] = i
	}
	fmt.Print(a)
}

结果:
在这里插入图片描述

练习7.3:fibonacci_array.go:

在第 6.6 节 我们看到了一个递归计算 Fibonacci 数值的方法。但是通过数组我们可以更快的计算出 Fibonacci 数。完成该方法并打印出前 50 个 Fibonacci 数字。

package main

import (
	"fmt"
	"time"
)

var fb [50]int

func fibonacci2(vua_1, idx_1, vua_2, idx_2 int) (vua_r, idx_r int) {
	if idx_2 == 2 {
		idx_r = idx_2 + 1
		vua_r = vua_2 + vua_1
		fmt.Print(idx_1, ":", vua_1, "\n")
		fmt.Print(idx_2, ":", vua_2, "\n")
		fibonacci2(vua_2, idx_2, vua_r, idx_r)
		return
	} else if idx_2 <= 50 {
		idx_r = idx_2 + 1
		vua_r = vua_2 + vua_1
		fmt.Print(idx_2, ":", vua_2, "\n")
		fibonacci2(vua_2, idx_2, vua_r, idx_r)
		return
	} else {
		return
	}
}

func main() {
	t1 := time.Now()
	fmt.Print(fb, "\n")
	for i, _ := range fb {
		if i == 0 || i == 1 {
			fb[i] = 1
		} else {
			fb[i] = fb[i-1] + fb[i-2]
		}
	}
	fmt.Print(fb, "\n")
	t2 := time.Now()
	delta := t2.Sub(t1)
	fmt.Printf("longCalculation took this amount of time: %s\n", delta)

	t3 := time.Now()
	fibonacci2(1, 1, 1, 2)
	t4 := time.Now()
	delta1 := t4.Sub(t3)
	fmt.Printf("longCalculation took this amount of time: %s\n", delta1)
}

结果:(数组比递归快)
在这里插入图片描述
在这里插入图片描述

数组初始化方法

第一种变化:

var arrAge = [5]int{18, 20, 15, 22, 16}
var arrAge =[10]int{18, 20, 15, 22, 16}//初始化前五个数

第二种变化:

var arrLazy = [...]int{5, 6, 7, 8, 22}

… 同样可以忽略,从技术上说它们其实变成了切片
第三种变化:key: value 语法

var arrKeyValue = [5]string{3: "Chris", 4: "Ron"}

只有索引 3 和 4 被赋予实际的值,其他元素都被设置为空的字符串

切片

和数组不同的是,切片的长度可以在运行时修改,最小为 0, 最大为相关数组的长度:切片是一个 长度可变的数组。
切片提供了计算容量的函数 cap() 可以测量切片最长可以达到多少:它等于切片的长度 + 数组除切片之外的长度。
多个切片如果表示同一个数组的片段,它们可以共享数据;因此一个切片和相关数组的其他切片是共享存储的,相反,不同的数组总是代表不同的存储。数组实际上是切片的构建块。

切片的声明和初始化方法

声明切片的格式是:

var identifier []type//(不需要说明长度)。

一个切片在未初始化之前默认为 nil,长度为 0。
初始化方式是:

var slice1 []type = arr1[start:end]

一个由数字 1、2、3 组成的切片可以这么生成:

s := [3]int{1,2,3}[:]

(注:应先用 s := [3]int{1, 2, 3} 生成数组, 再使用 s[:] 转成切片)甚至更简单的

 s := []int{1,2,3}

一个切片 s 可以这样扩展到它的大小上限,如果再扩大的话就会导致运行时错误

s = s[:cap(s)]
问题 7.2

给定切片 b:= []byte{‘g’, ‘o’, ‘l’, ‘a’, ‘n’, ‘g’},那么 b[1:4]、b[:2]、b[2:] 和 b[:] 分别是什么?

 package main

import "fmt"

func main() {
	b := []byte{'g', 'o', 'l', 'a', 'n', 'g'}
	fmt.Print(string(b), "\n")
	fmt.Print(string(b[1:4]), "\n")
	fmt.Print(string(b[:2]), "\n")
	fmt.Print(string(b[2:]), "\n")
	fmt.Print(string(b[1:]), "\n")

}

结果:
在这里插入图片描述

用 make() 创建切片

当相关数组还没有定义时,我们可以使用 make() 函数来创建一个切片,同时创建好相关数组。
make() 接受 2 个参数:元素的类型以及切片的元素个数。

var slice1 []type = make([]type, len)
slice1 := make([]type, len)
练习 7.4

fibonacci_funcarray.go: 为练习 7.3 写一个新的版本,主函数调用一个使用序列个数作为参数的函数,该函数返回一个大小为序列个数的 Fibonacci 切片。


 package main

import (
	"fmt"
)

var fb [50]int

func getF(num int) []int {
	return fb[:num]
}
func main() {
	for i, _ := range fb {
		if i == 0 || i == 1 {
			fb[i] = 1
		} else {
			fb[i] = fb[i-1] + fb[i-2]
		}
	}
	ff := getF(15)
	fmt.Print(ff)
}
new()和make()的区别

看起来二者没有什么区别,都在堆上分配内存,但是它们的行为不同,适用于不同的类型。

new(T) 为每个新的类型 T 分配一片内存,初始化为 0 并且返回类型为 *T 的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体它相当于 &T{}。
make(T) 返回一个类型为 T 的初始值,它只适用于 3 种内建的引用类型:切片、map 和 channel

换言之,new() 函数分配内存,make() 函数初始化;下图给出了区别:
在这里插入图片描述

byte包

byte切片,包含一个十分有用的类型 Buffer:


import "bytes"

type Buffer struct {
	...
}

这是一个长度可变的 bytes 的 buffer,提供 Read() 和 Write() 方法,因为读写长度未知的 bytes 最好使用 buffer。
Buffer 可以这样定义:

var buffer bytes.Buffer

或者使用 new() 获得一个指针:

var r *bytes.Buffer = new(bytes.Buffer)

或者通过函数:

func NewBuffer(buf []byte) *Buffer

通过 buffer 串联字符串,类似于 Java 的 StringBuilder 类。

在下面的代码段中,我们创建一个 buffer,通过 buffer.WriteString(s) 方法将字符串 s 追加到后面,最后再通过 buffer.String() 方法转换为 string:

var buffer bytes.Buffer
for {
	if s, ok := getNextString(); ok { //method getNextString() not shown here
		buffer.WriteString(s)
	} else {
		break
	}
}
fmt.Print(buffer.String(), "\n")

这种实现方式比使用 += 要更节省内存和 CPU,尤其是要串联的字符串数目特别多的时候。

练习 7.5

给定切片 sl,将一个 []byte 数组追加到 sl 后面。写一个函数 Append(slice, data []byte) []byte,该函数在 sl 不能存储更多数据的时候自动扩容。

package main

import (
	"bytes"
	"fmt"
)

func Append(slice, data []byte) []byte {
	var buffer bytes.Buffer
	buffer.Write(slice)
	for _, v := range data {
		buffer.WriteByte(v)
	}
	num := len(slice) + len(data)
	sl := make([]byte, num)
	buffer.Read(sl)
	return sl
}
func main() {
	var nums = [10]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	sl := nums[:5]
	sl1 := nums[5:]
	sl2 := Append(sl, sl1)
	fmt.Print(sl, "\n")
	fmt.Print(sl1, "\n")
	fmt.Print(sl2, "\n")

}

结果:
在这里插入图片描述

练习 7.6

把一个缓存 buf 分片成两个切片:第一个是前 n 个 bytes,后一个是剩余的,用一行代码实现。

import (
	"bytes"
	"fmt"
)

func main() {
	var nums = [10]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	var buffer bytes.Buffer
	sl := nums[:]
	buffer.Write(sl)
	//一行代码:
	pre_sl, end_sl := buffer.Bytes()[:5], buffer.Bytes()[5:]
	fmt.Print(pre_sl, "\n")
	fmt.Print(end_sl, "\n")

}

结果:
在这里插入图片描述

切片的for range
问题 7.6

通过使用省略号操作符 … 来实现累加方法。

package main

func add(a ...int) (res int) {
	for _, v := range a {
		res += v
	}
	return res
}
func main() {
	res := add(1, 2, 3, 4, 5, 6, 7, 8, 9)
	print(res)
}

在这里插入图片描述

练习 7.7 sum_array.go

a) 写一个 Sum() 函数,传入参数为一个 float32 数组成的数组 arrF,返回该数组的所有数字和。

如果把数组修改为切片的话代码要做怎样的修改?如果用切片形式方法实现不同长度数组的的和呢?

b) 写一个 SumAndAverage() 方法,返回两个 int 和 float32 类型的未命名变量的和与平均值。

package main

func sum(nums []float32) float32 {
	var res float32
	for _, v := range nums {
		res += v
	}
	return res
}
func SumAndAverange(a, b int, c float32) (sum, averge float32) {
	b_f, a_f := float32(b), float32(a)
	sum = b_f + a_f + c
	averge = sum / 3
	return
}
func main() {
	nums := [10]float32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	res := sum(nums[:])
	print(res, "\n")
	s, a := SumAndAverange(3, 5, 7.0)
	print(s, "\t", a)
}
练习 7.8 min_max.go

写一个 minSlice() 方法,传入一个 int 的切片并且返回最小值,再写一个 maxSlice() 方法返回最大值。
package main

func minSlice(a []int) (min int) {
	min = 100000000000000
	for _, v := range a {
		if v < min {
			min = v
		}
	}
	return
}
func maxSlice(a []int) (max int) {
	max = 0
	for _, v := range a {
		if v > max {
			max = v
		}
	}
	return
}
func main() {
	var nums = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	max := maxSlice(nums[:])
	min := minSlice(nums[:])
	print(max, "\t", min)
}
切片的复制和追加

复制copy()函数
将后面的切片的值复制到前一个切片

slFrom := []int{1, 2, 3}
slTo := make([]int, 10)
n := copy(slTo, slFrom)
fmt.Print(slFrom, "\t", slTo, "\t", n)

在这里插入图片描述
追加append()函数
func append(s[]T, x …T) []T 其中 append() 方法将 0 个或多个具有相同类型 s 的元素追加到切片后面并且返回新的切片;追加的元素必须和原切片的元素是同类型。如果 s 的容量不足以存储新增元素,append() 会分配新的切片来保证已有切片元素和新增元素的存储。因此,返回的切片可能已经指向一个不同的相关数组了。append() 方法总是返回成功,除非系统内存耗尽了。

如果你想将切片 y 追加到切片 x 后面,只要将第二个参数扩展成一个列表即可:x = append(x, y…)。

如果想追加字符串要将字符串转化为字节切片

s:=[]byte(string)

然后追加字符

s=append(s,string...)

append() 非常有用,它能够用于各种方面的操作:

将切片 b 的元素追加到切片 a 之后:

a = append(a, b...)

复制切片 a 的元素到新的切片 b 上:

b = make([]T, len(a))
copy(b, a)

删除位于索引 i 的元素:

a = append(a[:i], a[i+1:]...)

切除切片 a 中从索引 i 至 j 位置的元素:

a = append(a[:i], a[j:]...)

为切片 a 扩展 j 个元素长度:

a = append(a, make([]T, j)...)

在索引 i 的位置插入元素 x:

a = append(a[:i], append([]T{x}, a[i:]...)...)

在索引 i 的位置插入长度为 j 的新切片:

a = append(a[:i], append(make([]T, j), a[i:]...)...)

在索引 i 的位置插入切片 b 的所有元素:

a = append(a[:i], append(b, a[i:]...)...)

取出位于切片 a 最末尾的元素 x:

x, a = a[len(a)-1], a[:len(a)-1]

将元素 x 追加到切片 a:

a = append(a, x)
练习 7.9 magnify_slice.go

给定一个切片 s []int 和一个 int 类型的因子 factor,扩展 s 使其长度为 len(s) * factor。

package main

import "fmt"

func add(s []int, f int) []int {
	res := make([]int, len(s)*f)
	copy(res, s)
	return res
}
func main() {
	s := []int{1, 2, 3}
	factor := 5
	fmt.Print(s, "\n")
	s = add(s, factor)
	fmt.Print(s, "\n")

}

在这里插入图片描述

练习 7.10 filter_slice.go

用顺序函数过滤容器:s 是前 10 个整型的切片。构造一个函数 Filter,第一个参数是 s,第二个参数是一个 fn func(int) bool,返回满足函数 fn 的元素切片。通过 fn 测试方法测试当整型值是偶数时的情况。

package main

import "fmt"

func oshu(i int) bool {
	if i%2 == 0 {
		return true
	} else {
		return false
	}
}
func filter_slice(s []int, f func(int2 int) bool) []int {
	res := make([]int, 1)
	var ok bool = true
	for i, v := range s {
		if f(v) && ok {
			copy(res, s[i:i+1])
			ok = false
		} else if f(v) {
			res = append(res, s[i])

		}
	}
	return res
}
func main() {
	nums := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	ss := nums[:]
	aa := filter_slice(ss, oshu)
	fmt.Print(aa)

}

结果:
在这里插入图片描述

练习 7.11 insert_slice.go

写一个函数 InsertStringSlice() 将切片插入到另一个切片的指定位置。

package main

import "fmt"

func InsertStringSlice(sl_bein, sl_in []int, flag int) ([]int, bool) {
	if flag < len(sl_bein) {
		pre_sl, after_sl := sl_bein[:flag], sl_bein[flag:]
		res := append(pre_sl, sl_in...)
		res = append(res, after_sl...)
		return res, true
	} else {
		var res = make([]int, len(sl_bein)+len(sl_in))
		return res, false
	}

}
func main() {
	var num = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	s1 := num[:]
	s2 := num[3:7]
	res, ok := InsertStringSlice(s1, s2, 3)
	if ok {
		fmt.Print(res)
	}
}

结果:
在这里插入图片描述

练习 7.12 remove_slice.go

写一个函数 RemoveStringSlice() 将从 start 到 end 索引的元素从切片中移除。

package main

import "fmt"

func RemoveStringSlice(sl []int, begin, end int) ([]int, bool) {
	if end < len(sl) {
		before, _, after := sl[:begin], sl[begin:end+1], sl[end+1:]
		res := append(before, after...)
		return res, true
	} else {
		return sl, false
	}

}
func main() {
	var num = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	s1 := num[:]
	res, ok := RemoveStringSlice(s1, 2, 3)
	if ok {
		fmt.Print(res)
	}
}

结果:
在这里插入图片描述

字符串的修改

Go 语言中的字符串是不可变的,也就是说 str[index] 这样的表达式是不可以被放在等号左侧的。如果尝试运行 str[i] = ‘D’ 会得到错误:cannot assign to str[i]。

因此,您必须先将字符串转换成字节数组,然后再通过修改数组中的元素值来达到修改字符串的目的,最后将字节数组转换回字符串格式。

例如,将字符串 “hello” 转换为 “cello”:

s := "hello"
c := []byte(s)
c[0] = 'c'
s2 := string(c) // s2 == "cello"
字符串的比较

Compare() 函数会返回两个字节数组字典顺序的整数对比结果

func Compare(a, b[]byte) int {
    for i:=0; i < len(a) && i < len(b); i++ {
        switch {
        case a[i] > b[i]:
            return 1
        case a[i] < b[i]:
            return -1
        }
    }
    // 数组的长度可能不同
    switch {
    case len(a) < len(b):
        return -1
    case len(a) > len(b):
        return 1
    }
    return 0 // 数组相等
}
切片的排序和搜索

标准库提供了 sort 包来实现常见的搜索和排序操作。您可以使用 sort 包中的函数 func Ints(a []int) 来实现对 int 类型的切片排序。例如 sort.Ints(arri),其中变量 arri 就是需要被升序排序的数组或切片。为了检查某个数组是否已经被排序,可以通过函数 IntsAreSorted(a []int) bool 来检查,如果返回 true 则表示已经被排序。

 sort.Ints(arri)
 sort.IntsAreSorted(a []int) bool 

类似的,可以使用函数 func Float64s(a []float64) 来排序 float64 的元素,或使用函数 func Strings(a []string) 排序字符串元素。

sort.Float64s(arrf)
 sort.Float64sAreSorted(a []float64) bool 
 sort.Strings(arrf)
 sort.StringsAreSorted(a []string) bool 

想要在数组或切片中搜索一个元素,该数组或切片必须先被排序(因为标准库的搜索算法使用的是二分法)。然后,您就可以使用函数
func SearchInts 进行搜索,并返回对应结果的索引值。

sort.SearchInts(a []int, n int) int 
sort.SearchFloat64s(a []float64, n int) int 
sort.SearchStrings(a []string, n int) int 
练习 7.12 split_string.go

编写一个函数,要求其接受两个参数,原始字符串 str 和分割索引 i,然后返回两个分割后的字符串。

package main

import "fmt"

func split_string(s string, flag int) (s1, s2 string, ok bool) {
	ss := s[:]
	if flag < len(s) {
		s1 = ss[:flag]
		s2 = ss[flag:]
		ok = true
	} else {
		s1 = ss[:]
		s2 = ""
		ok = false
	}
	return
}
func split_string1(s string, flag int) (s1, s2 string, ok bool) {
	ss1 := []byte(s)
	if flag < len(s) {
		s1 = string(ss1[:flag])
		s2 = string(ss1[flag:])
		ok = true
	} else {
		s1 = string(ss1[:])
		s2 = ""
		ok = false
	}
	return
}
func main() {
	s := "dadahjkdahdkajs"

	s1, s2, ok := split_string(s, 5)
	if ok {
		fmt.Print(s1, "\n", s2, "\n")

	}
	s3, s4, ok1 := split_string1(s, 5)
	if ok1 {
		fmt.Print(s3, "\n", s4, "\n")

	}
}

结果:
在这里插入图片描述

练习 7.13 string_split2.go

假设有字符串 str,那么 str[len(str)/2:] + str[:len(str)/2] 的结果是什么?

package main

import “fmt”

func main() {
str := “qwertyuiop” //偶数
str1 := “qwertyuio” //奇数

ss := str[len(str)/2:] + str[:len(str)/2]
fmt.Print(str, "\n")
fmt.Print(ss, "\n")
ss1 := str1[len(str1)/2:] + str1[:len(str1)/2]
fmt.Print(str1, "\n")
fmt.Print(ss1, "\n")

}
结果:
在这里插入图片描述

练习 7.14 string_reverse.go

编写一个程序,要求能够反转字符串,即将 “Google” 转换成 “elgooG”(提示:使用 []byte 类型的切片)。

如果您使用两个切片来实现反转,请再尝试使用一个切片(提示:使用交换法)。

如果您想要反转 Unicode 编码的字符串,请使用 []int32 类型的切片。

package main

import "fmt"

func main() {
	str := "Google"
	ss := []byte(str)
	for i := 0; i < len(str)/2; i++ {
		ss[i], ss[len(str)-1-i] = ss[len(str)-1-i], ss[i]
	}
	s := string(ss)
	fmt.Print(s)
}

在这里插入图片描述

练习 7.15 Q29_uniq.go

编写一个程序,要求能够遍历一个字符数组,并将当前字符和前一个字符不相同的字符拷贝至另一个数组。

package main

import "fmt"

func check(s []byte) (res []byte) {
	res = append(res, s[0])
	for i := 1; i < len(s); i++ {
		if s[i] != s[i-1] {
			res = append(res, s[i])
		}
	}
	return
}
func main() {
	str := "Googleasdlkjkk;aassdhsjjd"
	ss := []byte(str)
	s := check(ss)
	fmt.Print(string(s))
}

结果:
在这里插入图片描述

练习 7.16 bubblesort.go

编写一个程序,使用冒泡排序的方法排序一个包含整数的切片(算法的定义可参考 维基百科)。

package main

import "fmt"

func bubblesort(a []int) []int {
	for i := 1; i < len(a); i++ {
		for j := 1; j < len(a)-i+1; j++ {
			if a[j] < a[j-1] {
				temp := a[j]
				a[j] = a[j-1]
				a[j-1] = temp
			}
		}
	}
	return a
}
func main() {
	num := []int{123, 456, 132, 456, 132, 65498, 123, 684, 1, 26}
	fmt.Print(num, "\n")
	num = bubblesort(num)
	fmt.Print(num, "\n")
}

结果:
在这里插入图片描述

练习 7.17 map_function.go

在函数式编程语言中,一个 map-function 是指能够接受一个函数原型和一个列表,并使用列表中的值依次执行函数原型,公式为:map ( F(), (e1,e2, . . . ,en) ) = ( F(e1), F(e2), … F(en) )。

编写一个函数 mapFunc 要求接受以下 2 个参数:

一个将整数乘以 10 的函数
一个整数列表
最后返回保存运行结果的整数列表。

package main

import "fmt"

func m10(a int) (res int) {
	res = a * 10
	return
}
func map_function(f func(int) int, a []int) []int {
	for i, v := range a {
		a[i] = f(v)
	}
	return a
}
func main() {
	num := []int{123, 456, 132, 456, 132, 65498, 123, 684, 1, 26}
	fmt.Print(num, "\n")
	num = map_function(m10, num)
	fmt.Print(num)
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值