标准库优先队列的push方法

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

Standard library Priority Queue push method

问题

下面的代码片段是优先队列的推送方法的库实现。我想知道为什么带有代码a = a[0 : n+1]的行不会抛出越界错误。

 func (pq *PriorityQueue) Push(x interface{}) {
    // Push and Pop use pointer receivers because they modify the slice's length,
    // not just its contents.
    // To simplify indexing expressions in these methods, we save a copy of the
    // slice object. We could instead write (*pq)[i].
    a := *pq
    n := len(a)
    a = a[0 : n+1]
    item := x.(*Item)
    item.index = n
    a[n] = item
    *pq = a
}
英文:

The code snippet below is the library implementation of the push methods for a priority queue. I am wondering why the line with the code a = a[0 : n+1] does not throw an out of bounds errors.

 func (pq *PriorityQueue) Push(x interface{}) {
	// Push and Pop use pointer receivers because they modify the slice's length,
	// not just its contents.
	// To simplify indexing expressions in these methods, we save a copy of the
	// slice object. We could instead write (*pq)[i].
	a := *pq
	n := len(a)
	a = a[0 : n+1]
	item := x.(*Item)
	item.index = n
	a[n] = item
	*pq = a
}

答案1

得分: 3

一个切片不是一个数组;它是对现有数组的一个视图。所讨论的切片由一个比它自身更大的数组支持。当你定义一个现有切片的切片时,实际上是在对底层数组进行切片,但所引用的索引是相对于源切片的。

这是一个很长的句子。我们将通过以下方式证明这一点:我们将创建一个长度为零的切片,但我们将强制底层数组更大。使用make创建切片时,第三个参数将设置底层数组的大小。表达式make([]int, 0, 2)将分配一个大小为2的数组,但它的值为大小为零的切片。

package main

import "fmt"

func main() {
    // 在一个大小为2的初始数组上创建一个零宽度切片
    a := make([]int, 0, 2)
    fmt.Println(a)

    // 扩展切片。由于我们没有超出初始数组的大小,所以这不会越界。
    a = a[0:len(a)+1]

    a[0] = 1
    fmt.Println(a)
    fmt.Println(a[0:len(a)+1])
}

在这里查看。您可以使用cap关键字引用支持给定切片的数组的大小。

你问到的特定代码在调用上下文中循环遍历cap(pq)(container/heap/example_test.go的第90行)。如果你修改调用点的代码并尝试将另一个项推入队列,它将像你期望的那样引发panic。我...可能不建议编写这样的代码。虽然标准库中的代码可以执行,但如果我在我的代码库中发现这样的代码,我会非常不满意。使用append关键字通常更安全。

英文:

a slice is not an array; it is a view onto an existing array. The slice in question is backed by an array larger than itself. When you define a slice of an existing slice, you're actually slicing the underlying array, but the indexes referenced are relative to the source slice.

That's a mouthful. Let's prove this in the following way: we'll create a slice of zero length, but we'll force the underlying array to be larger. When creating a slice with make, the third parameter will set the size of the underlying array. The expression make([]int, 0, 2) will allocate an array of size 2, but it evaluates to a size-zero slice.

package main

import ("fmt")

func main() {
    // create a zero-width slice over an initial array of size 2
    a := make([]int, 0, 2)
    fmt.Println(a)

    // expand the slice.  Since we're not beyond the size of the initial
    // array, this isn't out of bounds.
    a = a[0:len(a)+1]

    a[0] = 1
    fmt.Println(a)
    fmt.Println(a[0:len(a)+1])
}

see here. You can use the cap keyword to reference the size of the array that backs a given slice.

The specific code that you asked about loops over cap(pq) in the calling context (container/heap/example_test.go line 90). If you modify the code at the call site and attempt to push another item into the queue, it will panic like you expect. I ... probably wouldn't suggest writing code like this. Although the code in the standard library executes, I would be very sour if I found that in my codebase. It's generally safer to use the append keyword.

答案2

得分: 2

因为它在一个特定的示例程序中起作用。以下是来自原始/完整示例源代码的重要部分(http://golang.org/pkg/container/heap/#example_)

const nItem = 10

pq := make(PriorityQueue, 0, nItem)

for i := 0; i < cap(pq); i++ {
        item := &Item{
                value:    values[i],
                priority: priorities[i],
        }
        heap.Push(&pq, item)
}
英文:

Because it works in a specific example program. Here are the important parts from the original/full example source)

const nItem = 10

and

pq := make(PriorityQueue, 0, nItem)

and

for i := 0; i &lt; cap(pq); i++ {
        item := &amp;Item{
                value:    values[i],
                priority: priorities[i],
        }
        heap.Push(&amp;pq, item)
}

答案3

得分: 1

这是一个来自container/heap的示例吗?如果是的话,它不会抛出异常,因为容量足够大(请看Push方法的使用方式)。如果你将示例更改为Push更多的项超过容量,那么它将会抛出异常。

英文:

Is it an example from container/heap? If yes, then it doesn't throws an exception because capacity is big enough (see how the Push method is used). If you change the example to Push more items then the capacity, then it'll throw.

答案4

得分: 0

它通常是这样的;但在container/heap的例子中不是这样的。这是我之前给你的通用修复方法。

func (pq *PriorityQueue) Push(x interface{}) {
    a := *pq
    n := len(a)
    item := x.(*Item)
    item.index = n
    a = append(a, item)
    *pq = a
}

解决欧拉计划问题#81的Golang解决方案

英文:

It does in general; it doesn't in the container/heap example. Here's the general fix I already gave you some time ago.

func (pq *PriorityQueue) Push(x interface{}) {
    a := *pq
    n := len(a)
    item := x.(*Item)
    item.index = n
    a = append(a, item)
    *pq = a
}

Golang solution to Project Euler problem #81

huangapple
  • 本文由 发表于 2013年2月4日 00:28:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/14674621.html
匿名

发表评论

匿名网友

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

确定