小明说完谢谢后,回到了办公位继续工作。

一个小时后,我写完了支持整型的切片算法:

type Slice []intfunc NewSlice() Slice { return make(Slice, 0)}func (this* Slice) Add(elem int) error { for _, v := range *this { if v == elem { fmt.Printf("Slice:Add elem: %v already existn", elem) return ERR_ELEM_EXIST } } *this = append(*this, elem) fmt.Printf("Slice:Add elem: %v succn", elem) return nil}func (this* Slice) Remove(elem int) error { found := false for i, v := range *this { if v == elem { if i == len(*this) - 1 { *this = (*this)[:i] } else { *this = append((*this)[:i], (*this)[i+1:]...) } found = true break } } if !found { fmt.Printf("Slice:Remove elem: %v not existn", elem) return ERR_ELEM_NT_EXIST } fmt.Printf("Slice:Remove elem: %v succn", elem) return nil}

小明看了我的实现后,说:”我试用一下?"

“丑媳妇不怕见公婆。”我请他试用。

小明用了5分钟,写了下面的代码:

func main() { intSliceExec()}func intSliceExec() { fmt.Println("int slice start") slice := alg.NewSlice() slice.Add(1) fmt.Println("current int slice:", slice) slice.Add(2) fmt.Println("current int slice:", slice) slice.Add(2) fmt.Println("current int slice:", slice) slice.Add(3) fmt.Println("current int slice:", slice) slice.Remove(2) fmt.Println("current int slice:", slice) slice.Remove(2) fmt.Println("current int slice:", slice) slice.Remove(3) fmt.Println("current int slice:", slice) fmt.Println("int slice end")}

从试用代码中可以看出,整型数组切片中最多有三个元素[1 2 3],元素2插入的第二次应该失败,同理元素2删除的第二次也应该失败,整型数组切片最后只剩下一个元素[1]。

go run运行代码后,日志如下:

int slice startSlice:Add elem: 1 succcurrent int slice: [1]Slice:Add elem: 2 succcurrent int slice: [1 2]Slice:Add elem: 2 already existcurrent int slice: [1 2]Slice:Add elem: 3 succcurrent int slice: [1 2 3]Slice:Remove elem: 2 succcurrent int slice: [1 3]Slice:Remove elem: 2 not existcurrent int slice: [1 3]Slice:Remove elem: 3 succcurrent int slice: [1]int slice end

查看日志,结果符合期望。

需求二:切片算法支持字符串

周三下午,睡完午觉后精神有点小抖擞,浏览者邮件,突然发现公司又接了一个大单,于是吃了会精神食量。

”小哥,小哥!“

我抬头一看,是小雷。

”咋的啦,哥们?“我好奇的问道。

”听说你昨天实现了一个数组切片算法,已支持整型,我现在想用字符串型的数组切片算法,你能提供不?"小雷有点着急的问道。

我心里一想,Golang支持Any类型,即interface{},同时字符串和整型一样都可以直接用”==“运算符比较两个元素是否相等,所以你懂的。

”这个好实现,给我一首歌的时间就可以试用。“我说完后,就立刻修改起了代码。

两分钟后,我提供了新版本的代码:

type Slice []interface{}func NewSlice() Slice { return make(Slice, 0)}func (this* Slice) Add(elem interface{}) error { for _, v := range *this { if v == elem { fmt.Printf("Slice:Add elem: %v already existn", elem) return ERR_ELEM_EXIST } } *this = append(*this, elem) fmt.Printf("Slice:Add elem: %v succn", elem) return nil}func (this* Slice) Remove(elem interface{}) error { found := false for i, v := range *this { if v == elem { if i == len(*this) - 1 { *this = (*this)[:i] } else { *this = append((*this)[:i], (*this)[i+1:]...) } found = true break } } if !found { fmt.Printf("Slice:Remove elem: %v not existn", elem) return ERR_ELEM_NT_EXIST } fmt.Printf("Slice:Remove elem: %v succn", elem) return nil}

不难发现,改动很简单,只将三个地方的int改成了interface{},一切都是这么自然。

”哇塞,这么快?半首歌我还没听完。“小雷开森的说。

”简单设计,呵呵!“我们不约而同的说出了这个大家最爱说又最难做到的XP实践。

这次有了拷贝这个强大的武器,小雷两分钟就写完了试用代码:

func main() { intSliceExec() fmt.Println("") stringSliceExec()}func stringSliceExec() { fmt.Println("string slice start") slice := alg.NewSlice() slice.Add("hello") fmt.Println("current string slice:", slice) slice.Add("golang") fmt.Println("current string slice:", slice) slice.Add("golang") fmt.Println("current string slice:", slice) slice.Add("generic") fmt.Println("current string slice:", slice) slice.Remove("golang") fmt.Println("current string slice:", slice) slice.Remove("golang") fmt.Println("current string slice:", slice) slice.Remove("generic") fmt.Println("current string slice:", slice) fmt.Println("string slice end")}...

从试用代码中可以看出,字符串型数组切片中最多有三个元素[hello golang generic],元素golang插入的第二次应该失败,同理元素golang删除的第二次也应该失败,字符串型数组切片最后只剩下一个元素[hello]。

int slice startSlice:Add elem: 1 succcurrent int slice: [1]Slice:Add elem: 2 succcurrent int slice: [1 2]Slice:Add elem: 2 already existcurrent int slice: [1 2]Slice:Add elem: 3 succcurrent int slice: [1 2 3]Slice:Remove elem: 2 succcurrent int slice: [1 3]Slice:Remove elem: 2 not existcurrent int slice: [1 3]Slice:Remove elem: 3 succcurrent int slice: [1]int slice endstring slice startSlice:Add elem: hello succcurrent string slice: [hello]Slice:Add elem: golang succcurrent string slice: [hello golang]Slice:Add elem: golang already existcurrent string slice: [hello golang]Slice:Add elem: generic succcurrent string slice: [hello golang generic]Slice:Remove elem: golang succcurrent string slice: [hello generic]Slice:Remove elem: golang not existcurrent string slice: [hello generic]Slice:Remove elem: generic succcurrent string slice: [hello]string slice end

查看日志,结果符合期望。

需求三:切片算法支持用户自定义的类型

今天周四,眼看明天就周五了,打算中午出去吃个自助餐提高一下生活质量,于是叫着小方开着车就杀出去了。由于在一点半之前要回到公司上班,所以匆匆地找了一家自助餐店。

“哇靠,人真多!”小方这样感叹道。

“这个店应该搞成多种模式,比如选取大家常吃的几种套餐(A,C,D),这样百分之七十的上班族都会直接领套餐,就不会白白浪费排队时间了。”我不着边际的边想边说。

“自助餐还是更有吸引力,顾客可以任意搭配,做到真正的私人订制,而套餐吃几次就腻味了。”小方反驳着对我说。

...

紧赶慢赶,终于,终于在一点半前回到了公司,于是又开始编码了。

“小哥,听说你实现了一个数组切片算法,既支持整型,又支持字符串型,我这还有一个小小需求。”

我抬头一看,是小方,就问“啥子需求?“

”我这边有自定义的struct类型,也想用数组切片算法。“小方大方的提出需求。

”这个嘛,这个嘛,有点难度!“我边思考边回应:”给我半个小时,让我试试。“

”好的,小哥。“小方说完后露出了惬意的笑。

我们先自定义一个类型:

type Student struct { id string name string}

Student类型有两个数据成员,即id和name。id是学号,全局我唯一;name是中文名字的拼音,可重复。

用户自定义类型和基本类型(int或string)不同的是两个元素是否相等的判断方式不一样:

1.基本类型(int或string)直接通过”==“运算符来判断;

2.用户自定义类型万千种种,数组切片算法中不可能知道,所以需要通过interface提供的方法进行两个元素是否相等的判断。

我们接着定义一个interface:

type Comparable interface { IsEqual(obj interface{}) bool}

只要用户自定义的类型实现了接口Comparable,就可以调用它的方法IsEqual进行两个元素是否相等的判断了,于是我们实现了Student类型的IsEqual方法:

func (this Student) IsEqual(obj interface{}) bool { if student, ok := obj.(Student); ok { return this.GetId() == student.GetId() } panic("unexpected type")}func (this Student) GetId() string { return this.id}

用户自定义的GetId方法是必要的,因为Id不一定就是数据成员,可能是由多个数据成员拼接而成。

我们将数组切片算法的易变部分”v == elem"抽出来封装成方法:

func isEqual(a, b interface{}) bool { return a == b}

于是数组切片的Add方法和Remove方法就变成:

func (this* Slice) Add(elem interface{}) error { for _, v := range *this { if isEqual(v, elem) { fmt.Printf("Slice:Add elem: %v already existn", elem) return ERR_ELEM_EXIST } } *this = append(*this, elem) fmt.Printf("Slice:Add elem: %v succn", elem) return nil}func (this* Slice) Remove(elem interface{}) error { found := false for i, v := range *this { if isEqual(v, elem) { if i == len(*this) - 1 { *this = (*this)[:i] } else { *this = append((*this)[:i], (*this)[i+1:]...) } found = true break } } if !found { fmt.Printf("Slice:Remove elem: %v not existn", elem) return ERR_ELEM_NT_EXIST } fmt.Printf("Slice:Remove elem: %v succn", elem) return nil}

于是数组切片算法对于支持用户自定义类型的改动仅仅局限于isEqual函数了,我们通过接口查询来完成代码修改:

func isEqual(a, b interface{}) bool { if comparable, ok := a.(Comparable); ok { return comparable.IsEqual(b) } else { return a == b }}

半个小时后,我完成了代码,叫小方过来试用。

因为有拷贝这个强大的武器,小雷三分钟就写完了试用代码:

func main() { intSliceExec() fmt.Println("") stringSliceExec() fmt.Println("") structSliceExec()}func structSliceExec() { fmt.Println("struct slice start") xiaoMing := Student{"1001", "xiao ming"} xiaoLei := Student{"1002", "xiao lei"} xiaoFang := Student{"1003", "xiao fang"} slice := alg.NewSlice() slice.Add(xiaoMing) fmt.Println("current struct slice:", slice) slice.Add(xiaoLei) fmt.Println("current struct slice:", slice) slice.Add(xiaoLei) fmt.Println("current struct slice:", slice) slice.Add(xiaoFang) fmt.Println("current struct slice:", slice) slice.Remove(xiaoLei) fmt.Println("current struct slice:", slice) slice.Remove(xiaoLei) fmt.Println("current struct slice:", slice) slice.Remove(xiaoFang) fmt.Println("current struct slice:", slice) fmt.Println("struct slice end")}...

从试用代码中可以看出,用户自定义类型的数组切片中最多有三个元素[{1001 xiao ming} {1002 xiao lei} {1003 xiao fang}],元素{1002 xiao lei}插入的第二次应该失败,同理元素{1002 xiao lei}删除的第二次也应该失败,用户自定义类型的数组切片最后只剩下一个元素[{1001 xiao ming}]。

int slice startSlice:Add elem: 1 succcurrent int slice: [1]Slice:Add elem: 2 succcurrent int slice: [1 2]Slice:Add elem: 2 already existcurrent int slice: [1 2]Slice:Add elem: 3 succcurrent int slice: [1 2 3]Slice:Remove elem: 2 succcurrent int slice: [1 3]Slice:Remove elem: 2 not existcurrent int slice: [1 3]Slice:Remove elem: 3 succcurrent int slice: [1]int slice endstring slice startSlice:Add elem: hello succcurrent string slice: [hello]Slice:Add elem: golang succcurrent string slice: [hello golang]Slice:Add elem: golang already existcurrent string slice: [hello golang]Slice:Add elem: generic succcurrent string slice: [hello golang generic]Slice:Remove elem: golang succcurrent string slice: [hello generic]Slice:Remove elem: golang not existcurrent string slice: [hello generic]Slice:Remove elem: generic succcurrent string slice: [hello]string slice endstruct slice startSlice:Add elem: {1001 xiao ming} succcurrent struct slice: [{1001 xiao ming}]Slice:Add elem: {1002 xiao lei} succcurrent struct slice: [{1001 xiao ming} {1002 xiao lei}]Slice:Add elem: {1002 xiao lei} already existcurrent struct slice: [{1001 xiao ming} {1002 xiao lei}]Slice:Add elem: {1003 xiao fang} succcurrent struct slice: [{1001 xiao ming} {1002 xiao lei} {1003 xiao fang}]Slice:Remove elem: {1002 xiao lei} succcurrent struct slice: [{1001 xiao ming} {1003 xiao fang}]Slice:Remove elem: {1002 xiao lei} not existcurrent struct slice: [{1001 xiao ming} {1003 xiao fang}]Slice:Remove elem: {1003 xiao fang} succcurrent struct slice: [{1001 xiao ming}]struct slice end

查看日志,结果符合期望。

小结