2014-01-16 87 views
2

的行爲,我有這樣的測試代碼,只是從INT切片刪除偶數:奇怪golang範圍內表達

package main 

import "fmt" 

func main() { 
    a := []int{0, 1, 2, 3} 
    for i, v := range a { 
     fmt.Printf("i: %d v: %d\n", i, v) 
     fmt.Println("before", a) 
     if v%2 == 0 { 
      // delete a[i] 
      a = append(a[:i], a[i+1:]...) 
     } 
     fmt.Println("after", a, "\n") 
    } 
    fmt.Println("final", a) 

} 

輸出是:

i: 0 v: 0 
before [0 1 2 3] 
after [1 2 3] 

i: 1 v: 2 
before [1 2 3] 
after [1 3] 

i: 2 v: 3 
before [1 3] 
after [1 3] 

i: 3 v: 3 
before [1 3] 
after [1 3] 

final [1 3] 

您還可以在這裏http://play.golang.org/p/BFPxekBggS找到它。我的問題是爲什麼在最後兩次迭代中變量v的計算結果爲3?提前致謝。

回答

5

在內部中,片是像包含三個元件的結構體:

  • 後備Array
  • 背襯陣列的大小,其可以是訪問爲cap(slice)
  • 切片的長度,可以作爲len(slice)

在循環運行之前,a的支持陣列爲[0, 1, 2, 3],其中cap(a) == len(a) == 4

當修改a用下面的代碼:

a = append(a[:i], a[i+1:]...) 

a股原的背襯陣列,因爲新的長度小於所述容量的新值。因此,在第一次迭代中進行修改後,支持數組現在包含[1, 2, 3, 3]len(a) == 3。數組中的最後一個元素通過正常的切片操作不可見,但保留其舊值。

在第二次迭代中,切片再次縮短,因此後備陣列現在爲[1, 3, 3, 3]len(a) == 2

現在,當循環運行時,range表達式僅被計算一次,所以無論循環中做了哪些更改,它總是會導致4次迭代。它也將返回來自同一個支持數組的結果,這解釋了您所看到的數字。

+0

容易理解,謝謝 – mawenbao

4

問題是,當您在迭代它時正在修改a(刪除元素),所以結果會有點令人驚訝。我的猜測是,你的第一個刪除後,a是類似的東西在內存:[1 2 3 3],所以a[2]3,和你的第二個刪除後,a是類似的東西在內存:[1 3 3 3],所以a[3]3

所以,我的第一個建議是改變使用傳統for循環,而不是範圍內的代碼,並增加i只有當你不刪除的東西:

package main 

import "fmt" 

func main() { 
    a := []int{0, 1, 2, 3} 
    for i := 0; i < len(a); { 
     v := a[i] 
     fmt.Printf("i: %d v: %d\n", i, v) 
     fmt.Println("before", a) 
     if v%2 == 0 { 
      // delete a[i] 
      a = append(a[:i], a[i+1:]...) 
     } else { 
      i++ 
     } 
     fmt.Println("after", a, "\n") 
    } 
    fmt.Println("final", a) 

} 

這裏是輸出:

i: 0 v: 0 
before [0 1 2 3] 
after [1 2 3] 

i: 0 v: 1 
before [1 2 3] 
after [1 2 3] 

i: 1 v: 2 
before [1 2 3] 
after [1 3]                                                                                                                                    

i: 1 v: 3                                                                                                                                     
before [1 3]                                                                 
after [1 3]                                                                  

final [1 3] 

我的第二個建議是逆循環(從端迭代),以避免用於遞增/遞減的「特殊情況」:

package main 

import "fmt" 

func main() { 
    a := []int{0, 1, 2, 3} 
    for i := len(a) - 1; i >= 0; i-- { 
     v := a[i] 
     fmt.Printf("i: %d v: %d\n", i, v) 
     fmt.Println("before", a) 
     if v%2 == 0 { 
      // delete a[i] 
      a = append(a[:i], a[i+1:]...) 
     } 
     fmt.Println("after", a, "\n") 
    } 
    fmt.Println("final", a) 

} 

這裏是輸出:

i: 3 v: 3                                                                 
before [0 1 2 3]                                                                
after [0 1 2 3]                                                                 

i: 2 v: 2                                                                  
before [0 1 2 3]                                                                
after [0 1 3]                                                                 

i: 1 v: 1                                                                  
before [0 1 3]                                                                 
after [0 1 3]                                                                 

i: 0 v: 0                                                                  
before [0 1 3]                                                                 
after [1 3]                                                                  

final [1 3] 
+0

+1對於建議 – mawenbao