2014-01-16 2 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

Внутри кусочек подобен структуры, содержащей три элемента:

  • Массив подложки
  • Размер массива подложки, который может быть доступный как 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

Смежные вопросы