英文:
Looking for reasonable stack implementation in golang?
问题
到目前为止,我的天真方法是
type stack []int
func (s *stack) Push(v int) {
*s = append(*s, v)
}
func (s *stack) Pop() int {
res := (*s)[len(*s)-1]
*s = (*s)[:len(*s)-1]
return res
}
它可以工作- playground,但看起来很丑并且有太多的解引用。我能做得更好吗?
英文:
So far my naive approach is
type stack []int
func (s *stack) Push(v int) {
*s = append(*s, v)
}
func (s *stack) Pop() int {
res:=(*s)[len(*s)-1]
*s=(*s)[:len(*s)-1]
return res
}
it works - playground, but looks ugly and has too much dereferencing. Can I do better?
答案1
得分: 91
这是一种风格和个人口味的问题,你的代码很好(除了不是线程安全的,并且在从空栈中弹出时会出现恐慌)。为了简化代码,你可以使用值方法并返回栈本身,这在某种程度上更加优雅(对某些人来说)。例如:
type stack []int
func (s stack) Push(v int) stack {
return append(s, v)
}
func (s stack) Pop() (stack, int) {
// FIXME: 但是如果栈为空怎么办?
l := len(s)
return s[:l-1], s[l-1]
}
func main(){
s := make(stack,0)
s = s.Push(1)
s = s.Push(2)
s = s.Push(3)
s, p := s.Pop()
fmt.Println(p)
}
另一种方法是将其封装在一个结构体中,这样你还可以轻松地添加互斥锁以避免竞态条件等。代码如下:
type stack struct {
lock sync.Mutex // 如果不需要线程安全,可以不加锁
s []int
}
func NewStack() *stack {
return &stack {sync.Mutex{}, make([]int,0), }
}
func (s *stack) Push(v int) {
s.lock.Lock()
defer s.lock.Unlock()
s.s = append(s.s, v)
}
func (s *stack) Pop() (int, error) {
s.lock.Lock()
defer s.lock.Unlock()
l := len(s.s)
if l == 0 {
return 0, errors.New("Empty Stack")
}
res := s.s[l-1]
s.s = s.s[:l-1]
return res, nil
}
func main(){
s := NewStack()
s.Push(1)
s.Push(2)
s.Push(3)
fmt.Println(s.Pop())
fmt.Println(s.Pop())
fmt.Println(s.Pop())
}
英文:
It's a matter of style and personal taste, your code is fine (apart from not being thread safe and panicking if you pop from an empty stack). To simplify it a bit you can work with value methods and return the stack itself, it's slightly more elegant to some tastes. i.e.
type stack []int
func (s stack) Push(v int) stack {
return append(s, v)
}
func (s stack) Pop() (stack, int) {
// FIXME: What do we do if the stack is empty, though?
l := len(s)
return s[:l-1], s[l-1]
}
func main(){
s := make(stack,0)
s = s.Push(1)
s = s.Push(2)
s = s.Push(3)
s, p := s.Pop()
fmt.Println(p)
}
Another approach is to wrap it in a struct, so you can also easily add a mutex to avoid race conditions, etc. something like:
type stack struct {
lock sync.Mutex // you don't have to do this if you don't want thread safety
s []int
}
func NewStack() *stack {
return &stack {sync.Mutex{}, make([]int,0), }
}
func (s *stack) Push(v int) {
s.lock.Lock()
defer s.lock.Unlock()
s.s = append(s.s, v)
}
func (s *stack) Pop() (int, error) {
s.lock.Lock()
defer s.lock.Unlock()
l := len(s.s)
if l == 0 {
return 0, errors.New("Empty Stack")
}
res := s.s[l-1]
s.s = s.s[:l-1]
return res, nil
}
func main(){
s := NewStack()
s.Push(1)
s.Push(2)
s.Push(3)
fmt.Println(s.Pop())
fmt.Println(s.Pop())
fmt.Println(s.Pop())
}
答案2
得分: 21
这是一个使用链式数据结构实现的后进先出(LIFO)的代码实现。
package stack
import "sync"
type element struct {
data interface{}
next *element
}
type stack struct {
lock *sync.Mutex
head *element
Size int
}
func (stk *stack) Push(data interface{}) {
stk.lock.Lock()
element := new(element)
element.data = data
temp := stk.head
element.next = temp
stk.head = element
stk.Size++
stk.lock.Unlock()
}
func (stk *stack) Pop() interface{} {
if stk.head == nil {
return nil
}
stk.lock.Lock()
r := stk.head.data
stk.head = stk.head.next
stk.Size--
stk.lock.Unlock()
return r
}
func New() *stack {
stk := new(stack)
stk.lock = &sync.Mutex{}
return stk
}
这段代码实现了一个栈(stack)数据结构,使用链式数据结构实现了后进先出(LIFO)的特性。其中,Push
函数用于将元素压入栈中,Pop
函数用于从栈中弹出元素。New
函数用于创建一个新的栈对象。
英文:
Here is a LIFO implementation using linked data structure
package stack
import "sync"
type element struct {
data interface{}
next *element
}
type stack struct {
lock *sync.Mutex
head *element
Size int
}
func (stk *stack) Push(data interface{}) {
stk.lock.Lock()
element := new(element)
element.data = data
temp := stk.head
element.next = temp
stk.head = element
stk.Size++
stk.lock.Unlock()
}
func (stk *stack) Pop() interface{} {
if stk.head == nil {
return nil
}
stk.lock.Lock()
r := stk.head.data
stk.head = stk.head.next
stk.Size--
stk.lock.Unlock()
return r
}
func New() *stack {
stk := new(stack)
stk.lock = &sync.Mutex{}
return stk
}
答案3
得分: 6
我相信如果我们能够利用Go的标准库而不是像@guy_fawkes那样创建自定义数据结构,会更好。尽管他做得很好,但使用单链表不是一种标准的方式。
为了在O(1)的时间复杂度内获取链表的尾部,我会使用双向链表。我用空间换取时间。
我还采纳了@Not_a_Golfer的建议,在栈中添加了一个锁。
import (
"container/list"
"sync"
)
type Stack struct {
dll *list.List
mutex sync.Mutex
}
func NewStack() *Stack {
return &Stack{dll: list.New()}
}
func (s *Stack) Push(x interface{}) {
s.mutex.Lock()
defer s.mutex.Unlock()
s.dll.PushBack(x)
}
func (s *Stack) Pop() interface{} {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.dll.Len() == 0 {
return nil
}
tail := s.dll.Back()
val := tail.Value
s.dll.Remove(tail)
return val
}
点击这个playground预览结果。
英文:
I believe it would be nice if we could take advantage of Go's standard libs instead of creating self-defined data structure like @guy_fawkes did. Although he did a great job, it's not a standard way to use singly linked list.
To get list's tail in O(1) time complexity, I would use doubly linked list. I trade space for time.
I also take @Not_a_Golfer's advice to add a lock to the stack.
import (
"container/list"
"sync"
)
type Stack struct {
dll *list.List
mutex sync.Mutex
}
func NewStack() *Stack {
return &Stack{dll: list.New()}
}
func (s *Stack) Push(x interface{}) {
s.mutex.Lock()
defer s.mutex.Unlock()
s.dll.PushBack(x)
}
func (s *Stack) Pop() interface{} {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.dll.Len() == 0 {
return nil
}
tail := s.dll.Back()
val := tail.Value
s.dll.Remove(tail)
return val
}
Click this playground to preview the result.
答案4
得分: 4
引入泛型后,您现在可以创建一些非常优雅的实现,这里是一个使用更函数式风格的示例:
package main
import (
"fmt"
)
type stack[T any] struct {
Push func(T)
Pop func() T
Length func() int
}
func Stack[T any]() stack[T] {
slice := make([]T, 0)
return stack[T]{
Push: func(i T) {
slice = append(slice, i)
},
Pop: func() T {
res := slice[len(slice)-1]
slice = slice[:len(slice)-1]
return res
},
Length: func() int {
return len(slice)
},
}
}
func main() {
stack := Stack[string]()
stack.Push("this")
fmt.Println(stack.Length())
fmt.Println(stack.Pop())
}
这段代码使用泛型创建了一个名为stack
的结构体,其中包含了Push
、Pop
和Length
三个方法。通过调用Stack
函数,可以创建一个指定类型的栈。在main
函数中,我们创建了一个字符串类型的栈,并进行了一些操作。
英文:
With the introduction of generics you can now create some pretty elegant implementations, here's one using a more functional style:
package main
import (
"fmt"
)
type stack[T any] struct {
Push func(T)
Pop func() T
Length func() int
}
func Stack[T any]() stack[T] {
slice := make([]T, 0)
return stack[T]{
Push: func(i T) {
slice = append(slice, i)
},
Pop: func() T {
res := slice[len(slice)-1]
slice = slice[:len(slice)-1]
return res
},
Length: func() int {
return len(slice)
},
}
}
func main() {
stack := Stack[string]()
stack.Push("this")
fmt.Println(stack.Length())
fmt.Println(stack.Pop())
}
答案5
得分: 2
使用 https://github.com/emirpasic/gods
package main
import lls "github.com/emirpasic/gods/stacks/linkedliststack"
func main() {
stack := lls.New() // 空栈
stack.Push(1) // 1
stack.Push(2) // 1, 2
stack.Values() // 2, 1 (后进先出顺序)
_, _ = stack.Peek() // 2,true
_, _ = stack.Pop() // 2, true
_, _ = stack.Pop() // 1, true
_, _ = stack.Pop() // nil, false (没有可弹出的元素)
stack.Push(1) // 1
stack.Clear() // 空栈
stack.Empty() // true
stack.Size() // 0
}
英文:
Use https://github.com/emirpasic/gods
package main
import lls "github.com/emirpasic/gods/stacks/linkedliststack"
func main() {
stack := lls.New() // empty
stack.Push(1) // 1
stack.Push(2) // 1, 2
stack.Values() // 2, 1 (LIFO order)
_, _ = stack.Peek() // 2,true
_, _ = stack.Pop() // 2, true
_, _ = stack.Pop() // 1, true
_, _ = stack.Pop() // nil, false (nothing to pop)
stack.Push(1) // 1
stack.Clear() // empty
stack.Empty() // true
stack.Size() // 0
}
答案6
得分: 0
我可以做得更好吗?
有了泛型的发布,我们可以像这样泛化您的实现:
package main
import (
"fmt"
"log"
)
// 泛型栈
// 应该实现更好的约束条件
type stack[V any] []V
func (s *stack[V]) Push(v V) int {
*s = append(*s, v)
return len(*s)
}
func (s *stack[V]) Last() V {
l := len(*s)
// 由开发者处理空栈
if l == 0 {
log.Fatal("空栈")
}
last := (*s)[l-1]
return last
}
func (s *stack[V]) Pop() V {
removed := (*s).Last()
*s = (*s)[:len(*s)-1]
return removed
}
// 不需要指针,因为是只读操作
func (s stack[V]) Values() {
for i := len(s) - 1; i >= 0; i-- {
fmt.Printf("%v ", s[i])
}
}
英文:
> Can I do better?
With generics released, we can generalize your implementation like this:
package main
import (
"fmt"
"log"
)
// Generic stack
// One should implement better constraints
type stack[V any] []V
func (s *stack[V]) Push(v V) int {
*s = append(*s, v)
return len(*s)
}
func (s *stack[V]) Last() V {
l := len(*s)
// Upto the developer to handle an empty stack
if l == 0 {
log.Fatal("Empty Stack")
}
last := (*s)[l-1]
return last
}
func (s *stack[V]) Pop() V {
removed := (*s).Last()
*s = (*s)[:len(*s)-1]
return removed
}
// Pointer not needed because read-only operation
func (s stack[V]) Values() {
for i := len(s) - 1; i >= 0; i-- {
fmt.Printf("%v ", s[i])
}
}
答案7
得分: 0
我的堆栈实现:
优点:
- 使用“切片”不需要手动调整内存,自动调整!
- 对于“多线程”是安全的。
- 不容易破坏信息。
- 使用泛型,因此可以灵活地与不同类型的数据一起使用。
缺点:
- 无法删除或使用随机项。
stack := utility.NewStack[string]()
stack.Push("1")
stack2 := utility.NewStack[[]int]()
stack2.Push([]int{1, 2, 3, 4, 5})
element, _ := stack2.Peek()
// 使用泛型来定义堆栈中的类型,也可以使用结构体。
type Stack[T any] struct {
lock sync.Mutex // 用于线程安全的互斥锁
S []T // 切片
}
func NewStack[T any]() *Stack[T] {
return &Stack[T]{lock: sync.Mutex{}, S: []T{}}
}
func (stack *Stack[T]) Push(element T) {
stack.lock.Lock()
defer stack.lock.Unlock()
stack.S = append(stack.S, element)
}
func (stack *Stack[T]) Pop() (T, error) {
stack.lock.Lock()
defer stack.lock.Unlock()
l := len(stack.S)
if l == 0 {
var empty T
return empty, errors.New("empty Stack")
}
element := stack.S[l-1]
stack.S = stack.S[:l-1]
return element, nil
}
func (stack *Stack[T]) Peek() (T, error) {
stack.lock.Lock()
defer stack.lock.Unlock()
l := len(stack.S)
if l == 0 {
var empty T
return empty, errors.New("empty Stack")
}
return stack.S[l-1], nil
}
英文:
My Stack implimentation:
The advantages are:
- Use "slice" no need to retune memory is automatically tuned!
- It is safe for "multithreading"
- Not easy to corrupt information
- Generics are used so it is flexible in use with different types of data.
The disadvantages are:
- You can't remove, use an random item
stack := utility.NewStack[string]()
stack.Push("1")
stack2 := utility.NewStack[[]int]()
stack2.Push([]int{1, 2, 3, 4, 5})
element, _ := stack2.Peek()
// Using Generics to define Type in Stake to Use Structs, too.
type Stack[T any] struct {
lock sync.Mutex // Mutex for Thread safety
S []T // Slice
}
func NewStack[T any]() *Stack[T] {
return &Stack[T]{lock: sync.Mutex{}, S: []T{}}
}
func (stack *Stack[T]) Push(element T) {
stack.lock.Lock()
defer stack.lock.Unlock()
stack.S = append(stack.S, element)
}
func (stack *Stack[T]) Pop() (T, error) {
stack.lock.Lock()
defer stack.lock.Unlock()
l := len(stack.S)
if l == 0 {
var empty T
return empty, errors.New("empty Stack")
}
element := stack.S[l-1]
stack.S = stack.S[:l-1]
return element, nil
}
func (stack *Stack[T]) Peek() (T, error) {
stack.lock.Lock()
defer stack.lock.Unlock()
l := len(stack.S)
if l == 0 {
var empty T
return empty, errors.New("empty Stack")
}
return stack.S[l-1], nil
}
答案8
得分: 0
在Golang中并发安全栈实现中的另一个添加。
支持以下操作:
- Push(value interface{})
- Peek() interface{}
- Pop()
- Size() int
- Reverse()
- Display()
package main
import (
"fmt"
"sync"
)
type StackMethods interface {
Push(value interface{})
Peek() interface{}
Pop()
Size() int
Reverse()
Display()
}
type Stack struct {
node *Node
size int
lock sync.Mutex
}
type Node struct {
value interface{}
next *Node
}
func (s *Stack) Size() int {
return s.size
}
func (s *Stack) Push(value interface{}) {
s.lock.Lock()
defer s.lock.Unlock()
s.node = &Node{
value: value,
next: s.node,
}
s.size++
}
func (s *Stack) Peek() interface{} {
if s.size > 0 {
fmt.Println("Element: ", s.node.value)
return s.node.value
} else {
fmt.Println("No elements in stack to display.")
}
return nil
}
func (s *Stack) Pop() {
s.lock.Lock()
defer s.lock.Unlock()
if s.size > 0 {
fmt.Printf("Removing element %v from stack.\n", s.node.value)
s.node = s.node.next
s.size--
} else {
fmt.Println("No elements in stack to remove.")
}
}
func (s *Stack) Reverse() {
s.lock.Lock()
defer s.lock.Unlock()
if s.node != nil {
var next, prev *Node
curr := s.node
for curr != nil {
next = curr.next
curr.next = prev
prev = curr
curr = next
}
s.node = prev
return
}
}
func (s *Stack) Display() {
if s.node != nil {
ptr := s.node
fmt.Println("Displaying elements")
for ptr != nil {
fmt.Print(ptr.value, "\n")
fmt.Println("____")
ptr = ptr.next
}
return
}
fmt.Println("Stack is empty.")
}
func main() {
//Creating interface object.
var stackObject StackMethods
//Assigning Stack struct object to interface object.
stackObject = new(Stack)
stackObject.Push("A")
fmt.Println("Size:", stackObject.Size())
stackObject.Peek()
stackObject.Peek()
fmt.Println("Size:", stackObject.Size())
stackObject.Push("B")
stackObject.Push("C")
fmt.Println("Size:", stackObject.Size())
stackObject.Peek()
fmt.Println("Size:", stackObject.Size())
fmt.Println("Before Reversing:")
stackObject.Display()
fmt.Println("After Reversing:")
stackObject.Reverse()
stackObject.Display()
}
英文:
One more addition to concurrency safe stack implementation in Golang.
Supporting operations like:
- Push(value interface{})
- Peek() interface{}
- Pop()
- Size() int
- Reverse()
- Display()
package main
import (
"fmt"
"sync"
)
type StackMethods interface {
Push(value interface{})
Peek() interface{}
Pop()
Size() int
Reverse()
Display()
}
type Stack struct {
node *Node
size int
lock sync.Mutex
}
type Node struct {
value interface{}
next *Node
}
func (s *Stack) Size() int {
return s.size
}
func (s *Stack) Push(value interface{}) {
s.lock.Lock()
defer s.lock.Unlock()
s.node = &Node{
value: value,
next: s.node,
}
s.size++
}
func (s *Stack) Peek() interface{} {
if s.size > 0 {
fmt.Println("Element: ", s.node.value)
return s.node.value
} else {
fmt.Println("No elements in stack to display.")
}
return nil
}
func (s *Stack) Pop() {
s.lock.Lock()
defer s.lock.Unlock()
if s.size > 0 {
fmt.Printf("Removing element %v from stack.\n", s.node.value)
s.node = s.node.next
s.size--
} else {
fmt.Println("No elements in stack to remove.")
}
}
func (s *Stack) Reverse() {
s.lock.Lock()
defer s.lock.Unlock()
if s.node != nil {
var next, prev *Node
curr := s.node
for curr != nil {
next = curr.next
curr.next = prev
prev = curr
curr = next
}
s.node = prev
return
}
}
func (s *Stack) Display() {
if s.node != nil {
ptr := s.node
fmt.Println("Displaying elements")
for ptr != nil {
fmt.Print(ptr.value, "\n")
fmt.Println("____")
ptr = ptr.next
}
return
}
fmt.Println("Stack is empty.")
}
func main() {
//Creating interface object.
var stackObject StackMethods
//Assigning Stack struct object to interface object.
stackObject = new(Stack)
stackObject.Push("A")
fmt.Println("Size:", stackObject.Size())
stackObject.Peek()
stackObject.Peek()
fmt.Println("Size:", stackObject.Size())
stackObject.Push("B")
stackObject.Push("C")
fmt.Println("Size:", stackObject.Size())
stackObject.Peek()
fmt.Println("Size:", stackObject.Size())
fmt.Println("Before Reversing:")
stackObject.Display()
fmt.Println("After Reversing:")
stackObject.Reverse()
stackObject.Display()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论