Golang基础篇之数据结构-栈

本篇通过实现一个自定义的栈,来学习Go语言的自定义类型及其方法
首先栈的概念不用多说,它是一种支持从顶端插入或删除的线性表,废话少说上代码。
GOPATH下新建stack目录,栈的所有实现在stack.go文件之中。
首先需要一个能够保存数据的结构体

type Stack []interfice {}

这里声明Stack为空接口类型的切片(Go语言之中的切片可以理解为一个长度可变的数组)。由于Go语言所有类型都实现了空接口,因此任意类型的值都可以存储在Stack之中。
接下来,由于Stack的底层数据类型是一个切片,我们可以为其实现Len()Cap()方法用来获取其长度和容量(Go语言之中首字母大写的方法为包外可访问的,类似于Java或者C++之中类的public方法)。

func (stack Stack) Len() int {
    return len(stack)
}

方法的定义如上。func关键字用来定义方法,其后紧跟的是方法所作用的类型,这里这个Len()方法作用于Stack这个类型的值,该值通常被称为该方法的“接受器”(Go语言的类型声明,变量声明都将类型放在名称之后);然后是方法名以及参数列表,这里参数为空;最后面是方法的返回值,这里为int。方法的实现调用了Go语言的内建函数len()len()返回切片和数组的长度。
Cap()方法与Len()类似,其实现也调用了内建函数cap()来获取切片的长度

func (stack Stack) Cap() int {
    return cap(stack)
}

接下来实现其关键方法Push()Top()Pop()

func (stack *Stack) Push(value interface{})  {
    *stack = append(*stack, value)
}

Push()方法的接收器为一个Stack类型的指针(Go指针的写法与C/C++类似,类型前面加上*号)。Go语言的所有方法参数都是值传递,接收器实际也是作为方法的一个参数传递进入方法的。如果传递一切片或者数组进方法,实际是将切片或数组的值拷贝了一份传入了方法之中,此时在方法之中对该切片或数组做的操作都不会影响方法之外的原始值。如果想要方法之中的操作影响到方法外的原始值,则应该使用指针作为参数,对指针的操作会直接反应到内存之中的原始值上去。在这里我们希望更改原始值(往原始的stack之中添加数据), 所以接收器是一个指针。方法的参数是一个interface{}类型的值,也就是说该方法可以接受任意类型作为参数。方法的实现使用了内建函数append(),往切片对尾部中添加新值。

func (stack Stack) Top() (interface{}, error)  {
    if len(stack) == 0 {
        return nil, errors.New("Out of index, len is 0")
    }
    return stack[len(stack) - 1], nil
}

Top()方法返回一个任意类型的值以及一个error(是的没错,Go语言的方法可以返回多个值)。当stack为空时,返回一个空值和一个error类型的值(这里使用errors包的New()函数创建)。当stack不为空时,返回底层切片的最后一个值和一个空的error

func (stack *Stack) Pop() (interface{}, error)  {
    theStack := *stack
    if len(theStack) == 0 {
        return nil, errors.New("Out of index, len is 0")
    }
    value := theStack[len(theStack) - 1]
    *stack = theStack[:len(theStack) - 1]
    return value, nil
}

Pop()方法的接收器同样是一个Stack的指针。其中theStack[:len(theStack) - 1]这种写法,是Go中取子切片的方法,:两边是起始index和结束index。起始index为0时可以省略。结束的index也可以省略,省略时结束index为切片的len()值。
此外还有一个简单的IsEmpty()方法

func (stack Stack) IsEmpty() bool  {
    return len(stack) == 0
}

以上所有方法实现完毕,写一个stack_test.go来测试stack.go,在包目录下运行go test即可查看结果。测试通过即可使用go install命令将该stack包安装到GOPATH之中,然后就可以在其他项目引用自己实现的Stack
完整源码比较短,直接贴在这里
stack.go

package stack

import "errors"

type Stack []interface {}

func (stack Stack) Len() int {
    return len(stack)
}

func (stack Stack) IsEmpty() bool  {
    return len(stack) == 0
}

func (stack Stack) Cap() int {
    return cap(stack)
}

func (stack *Stack) Push(value interface{})  {
    *stack = append(*stack, value)
}

func (stack Stack) Top() (interface{}, error)  {
    if len(stack) == 0 {
        return nil, errors.New("Out of index, len is 0")
    }
    return stack[len(stack) - 1], nil
}

func (stack *Stack) Pop() (interface{}, error)  {
    theStack := *stack
    if len(theStack) == 0 {
        return nil, errors.New("Out of index, len is 0")
    }
    value := theStack[len(theStack) - 1]
    *stack = theStack[:len(theStack) - 1]
    return value, nil
}

stack_test.go

package stack

import (
    "testing"
)

func TestStack_Len(t *testing.T) {
    var myStack Stack
    myStack.Push(1)
    myStack.Push("test")
    if myStack.Len() == 2 {
        t.Log("Pass Stack.Len")
    } else {
        t.Error("Failed Stack.Len")
    }
}

func TestStack_IsEmpty(t *testing.T) {
    var mStack Stack
    if mStack.IsEmpty() {
        t.Log("Pass Stack.IsEmpty")
    } else {
        t.Error("Failed Stack.IsEmpty")
    }
}

func TestStack_Cap(t *testing.T) {
    myStack := make(Stack, 3)
    if myStack.Cap() == 3 {
        t.Log("Pass Stack.Cap")
    } else {
        t.Error("Failed Stack.Cap")
    }
}

func TestStack_Push(t *testing.T) {
    var mStack Stack
    mStack.Push(3)
    if mStack.Len() == 1 {
        t.Log("Pass Stack.Push")
    } else {
        t.Error("Failed Stack.Push")
    }
}

func TestStack_Top(t *testing.T) {
    var mStack Stack
    if _, err := mStack.Top(); err == nil {
        t.Error("Failed Stack.Top")
    }
    mStack.Push(3)
    if value, _ := mStack.Top(); value == 3 {
        t.Log("Pass Stack.Top")
    } else {
        t.Errorf("Failed Stack.Top, value is %d", value)
    }
}

func TestStack_Pop(t *testing.T) {
    var mStack Stack
    if _, err := mStack.Pop(); err == nil {
        t.Error("Failed Stack.Top")
    }
    mStack.Push("test")
    mStack.Push(3)
    if value, _ := mStack.Pop(); value == 3 && mStack.Len() == 1 {
        t.Log("Pass Stack.Pop")
    } else {
        t.Errorf("Failed Stack.Pop, value is %d, len is %d", value, mStack.Len())
    }
}
附注
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容

  • 出处---Go编程语言 欢迎来到 Go 编程语言指南。本指南涵盖了该语言的大部分重要特性 Go 语言的交互式简介,...
    Tuberose阅读 18,412评论 1 46
  • 官方网站:https://golang.org/标准库文档:https://golang.org/pkg/在线编码...
    技术学习阅读 2,324评论 2 39
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,605评论 18 399
  • 1.JSONP原理 ajax请求受同源策略影响,不允许进行跨域请求,而script标签src属性中的链接却可以访问...
    西瓜炒苦瓜阅读 308评论 0 1
  • 公司做的一款教育App中,有个BookReading模块是孩子用来学习英语,里面又有一个子模块Listen用来播放...
    XcqRomance阅读 1,225评论 0 5