How do I (succinctly) remove the first element from a slice in Go?

huangapple go评论84阅读模式
英文:

How do I (succinctly) remove the first element from a slice in Go?

问题

我已经在Go中构建了一个简单的队列。它使用一个内部切片来跟踪其元素。通过将元素追加到切片中,将元素推入队列。我想通过删除elements中的第一个元素来实现.Pop()。在许多其他语言中,"弹出"列表的第一个元素是一行代码,这让我相信我下面的实现方法可能有些冗长和啰嗦。有更好的方法吗?

请注意,如果len(queue.elements) == 0,我希望Queue引发panic。我没有检查边界并不是疏忽。

英文:

I've built a simple queue in Go. It uses an internal slice to keep track of its elements. Elements are pushed onto the queue by appending to the slice. I'd like to implement .Pop() by removing the first element in elements.

In many other languages, "popping" the first element of a list is a one-liner, which leads me to believe my implementation below is sloppy and verbose. Is there a better way?

type Queue struct {
	elements []interface{}
}

func (queue *Queue) Push(element interface{}) {
	queue.elements = append(queue.elements, element)
}

func (queue *Queue) Pop() interface{} {
	element := queue.elements[0]
	if len(queue.elements) > 1 {
		queue.elements = queue.elements[1:]
	} else {
		queue.elements = make([]interface{}, 0)
	}
	return element
}

Please note that I wish for the Queue to panic if len(queue.elements) == 0. It's not an oversight that I don't check the bounds.

答案1

得分: 260

你试过这些吗?

从队列中弹出元素

x, a = a[0], a[1:]

从栈中弹出元素

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

压入元素

a = append(a, x)

来源:https://code.google.com/p/go-wiki/wiki/SliceTricks

英文:

Did you try these?

Pop from queue

x, a = a[0], a[1:]

Pop from stack

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

Push

a = append(a, x)

From: https://code.google.com/p/go-wiki/wiki/SliceTricks

答案2

得分: 13

如果你想要一个环形缓冲区或FIFO结构,那么像@Everton的答案中使用切片会导致垃圾回收问题,因为底层数组可能会无限增长。

在Go语言中,最简单的方法是使用通道(channel),只要你不介意有限的大小,这也是安全的并发访问。在Go语言中,这是一个非常常见的习惯用法,通常不需要像下面的示例那样将其封装成一个类型。

例如(Playground):

package main

import "fmt"

type Queue struct {
    elements chan interface{}
}

func NewQueue(size int) *Queue {
    return &Queue{
        elements: make(chan interface{}, size),
    }
}

func (queue *Queue) Push(element interface{}) {
    select {
    case queue.elements <- element:
    default:
        panic("Queue full")
    }
}

func (queue *Queue) Pop() interface{} {
    select {
    case e := <-queue.elements:
        return e
    default:
        panic("Queue empty")
    }
    return nil
}

func main() {
    q := NewQueue(128)

    q.Push(1)
    q.Push(2)
    q.Push(3)
    fmt.Printf("Pop %d\n", q.Pop())
    fmt.Printf("Pop %d\n", q.Pop())
    fmt.Printf("Pop %d\n", q.Pop())
    fmt.Printf("Pop %d\n", q.Pop())

}

Playground

英文:

If you want a ring buffer or FIFO structure then using a slice as in @Everton's answer will cause garbage collection problems as the underlying array may grow indefinitely.

The easiest way to do this in go, provided you don't mind having a limited size, is to use a channel which is also safe for concurrent access. This is such a common idiom in go that you wouldn't usually bother wrapping it in a type like the below.

Eg (Playground)

package main

import &quot;fmt&quot;

type Queue struct {
	elements chan interface{}
}

func NewQueue(size int) *Queue {
	return &amp;Queue{
		elements: make(chan interface{}, size),
	}
}

func (queue *Queue) Push(element interface{}) {
	select {
	case queue.elements &lt;- element:
	default:
		panic(&quot;Queue full&quot;)
	}
}

func (queue *Queue) Pop() interface{} {
	select {
	case e := &lt;-queue.elements:
		return e
	default:
		panic(&quot;Queue empty&quot;)
	}
	return nil
}

func main() {
	q := NewQueue(128)

	q.Push(1)
	q.Push(2)
	q.Push(3)
	fmt.Printf(&quot;Pop %d\n&quot;, q.Pop())
	fmt.Printf(&quot;Pop %d\n&quot;, q.Pop())
	fmt.Printf(&quot;Pop %d\n&quot;, q.Pop())
	fmt.Printf(&quot;Pop %d\n&quot;, q.Pop())

}

huangapple
  • 本文由 发表于 2014年5月8日 10:48:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/23531891.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定