Промени в указателя на цикъла Go

Използвам for range цикъл в Go, за да итерирам през част от структури.

Във всеки цикъл аз указател към текущия елемент към променлива.

Объркан съм защо показалецът променя стойността си в следващия цикъл.

Например този код:

package main

import "fmt"

type t struct {
    val int
}

func main() {
    l := []t{{1}, {2}}
    var p *t
    for _, i := range l {
        fmt.Println("begin", p)
        p = &i
        fmt.Println("end", p)
    }
}

Очаквам да произведа:

begin <nil>
end &{1}
begin &{1}
end &{2}

Но всъщност прави:

begin <nil>
end &{1}
begin &{2}
end &{2}

За справка, в моя действителен код, аз проверявам за условие по време на цикъла и връщам текущия елемент и предишния. Така че се опитвам да запазя указател към него, така че в следващата итерация да има достъп и до предишния.


person saul.shanabrook    schedule 04.09.2015    source източник
comment
В началото на всяка итерация на цикъла for копира текущия елемент от l в i.   -  person Tim Cooper    schedule 04.09.2015
comment
Така че използва повторно един i основно за всички повторения? Мисля, че това има смисъл. Как бих запазил указател към действителния обект тогава, ако съм в своя цикъл?   -  person saul.shanabrook    schedule 04.09.2015


Отговори (3)


Проблемът е, че вземате адреса на променливата за цикъл/обхват, а не адреса на елемента в среза. Вие обаче просто правите много ненужна работа за себе си. Първо, защо не използвате i, v := range или още по-добре i, _ := и след това можете да направите i-1, за да получите предишния елемент? Второ, дори ако искате да бъде записан в указател, пак използвайте този синтаксис и след това присвоете p = &l[i], така че да имате адреса на елемента в среза, а не адреса на променливата за цикъл/обхват.

Хората са твърде нетърпеливи да използват for/each стилови конструкции, когато очевидно е по-добре да се работи с индекса... Ако искате индекс-1 на всяка итерация, използването на индекса трябва да е вашият начин да направите това.

person evanmcdonnal    schedule 04.09.2015

Изграждайки коментар на Тим, изглежда, че можете да копирате стойността на всеки цикъл, вместо на указателя и го дереферирайте след това.

package main

import "fmt"

type t struct {
    val int
}

func main() {
    l := []t{{1}, {2}}
    var p t
    var i t
    for _, i = range l {
        fmt.Println("begin", &p)
        p = i
        fmt.Println("end", &p)
    }
}
person saul.shanabrook    schedule 04.09.2015
comment
вижте тук за ЧЗВ: golang.org/doc/faq#closures_and_goroutines. Идиомата i := i е може би най-често срещаното решение, когато не използвате затваряне. - person JimB; 04.09.2015
comment
@JimB Благодаря, това е много подходящо. - person saul.shanabrook; 04.09.2015

Друг вариант е да получите показалеца към текущия елемент, като използвате индекса:

package main

import "fmt"

type t struct {
    val int
}

func main() {
    l := []t{{1}, {2}}
    var p *t

    for index, _ := range l {
        fmt.Println("begin", p)
        p = &l[index]
        fmt.Println("end", p)
    }
}
person saul.shanabrook    schedule 04.09.2015