2015-05-28 28 views
0

我使用Go的heap包創建優先級隊列。文檔中有一個an exampleGo堆。作爲結構的接口

我創建的隊列需要基於結構而不是切片,因爲它需要其他屬性(如互斥鎖)。

type PQueue struct { 
    queue []*Item 
    sync.Mutex 
} 

我執行heap.Interface要求的所有方法。

問題是我的PQueue.Push方法似乎不會永久性地爲PQueue.queue添加值。

func (p PQueue) Push(x interface{}) { 
    p.Lock() 
    defer p.Unlock() 
    item := x.(*Item) 
    item.place = len(p.queue) // the index of an item in the queue 
    p.queue = append(p.queue, item) 
    // len(p.queue) does increase 
    // after the functions exits, the queues length has not increased 
} 

如果我打印在這個函數結束時的p.queue長度,該長度增加了。然而,函數退出後,似乎原來的結構不會被更新。

我認爲這可能是因爲func (p PQueue)不是指針而發生的。爲什麼會這樣?有沒有辦法解決它?如果我使用func (p *PQeueue) Push(x interface{})代替,我需要實現我自己的堆,因爲heap.Interface明確地不需要指針。這是我唯一的選擇嗎?

+0

那麼,heap.Interface'不需要指針。指針類型可以實現一個接口('* os.File'是一個'io.Reader')。嘗試一下,如果它無法解決問題,請發表另一個問題;有人會試圖幫助解決它。 – twotwotwo

回答

2

對於與您的Push方法的接收方相關的問題,您是正確的:該方法將收到PQueue的副本,因此對該結構所做的任何更改都不會持續。

更改使用指針作爲接收器的方法是正確的更改,但這也意味着PQueue不再實現heap.Interface。這是由於Go不允許您將指針指向存儲在接口變量中的值,因此不會將q.Push()自動轉換爲(&q).Push()

雖然這不是死路一條,因爲*PQueue仍然應該執行heap.Interface。因此,如果您以前撥打heap.Init(q),只需將其更改爲heap.Init(&q)即可。

0

我想可能是因爲FUNC(P PQueue)來發生的事情不是一個指針

這是正確的。引述Effective Go

調用[方法]上的值將導致該方法以接收所述值的 副本,所以任何修改將被丟棄。

你說:

heap.Interface明確要求沒有指針

我很困惑,你指向就是,例如事實上,使用指針:

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

也許別的事情正在發生?

2

問題是您正在追加到您的切片的副本。因此,更改顯示在函數中,但一旦從函數返回,將會丟失。

this blog article從部分傳遞切片功能

明白,即使片包含 指針,它本身就是一個價值是非常重要的。在封面下,它是一個包含指針和長度的結構值 。它不是一個指向結構體的指針。

隨着附加,您正在修改切片標頭。而

因此,如果我們想編寫修改頭的功能,我們必須 其返回結果參數

或者:

另一種方法是有一個功能修改片頭將傳遞一個 指針給它。

因此,如果你想用append修改它,你需要傳遞一個指針。只需更改方法以使用指針接收器即可。爲了實現這個目標,你需要使用像heap.Init(&pq)這樣的指針來調用init,如你鏈接的例子所示,並且它也使用指針接收器。

spec的方法設置

對應的指針類型* T的方法集是一組與接收器聲明的所有方法 * T或T(即,它也含有方法 設置T)。

因此,使用指針類型將與值和指針接收器一起工作,並仍然實現接口。