web-dev-qa-db-fra.com

Existe-t-il un moyen d’itérer sur une plage d’entiers dans Golang?

La plage de Golang peut parcourir les cartes et les tranches, mais je me demandais s’il existait un moyen de parcourir une plage de nombres, quelque chose comme ceci.

for i := range [1..10] {
    fmt.Println(i)
}

ou y a-t-il un moyen de représenter une plage d'entiers dans Go comme Ruby?

118
Vishnu

Vous pouvez et devriez simplement écrire une boucle for. Le code simple et évident est le moyen d'aller.

for i := 1; i <= 10; i++ {
    fmt.Println(i)
}
160
Paul Hankin

Voici un programme pour comparer les deux manières suggérées jusqu'ici

import (
    "fmt"

    "github.com/bradfitz/iter"
)

func p(i int) {
    fmt.Println(i)
}

func plain() {
    for i := 0; i < 10; i++ {
        p(i)
    }
}

func with_iter() {
    for i := range iter.N(10) {
        p(i)
    }
}

func main() {
    plain()
    with_iter()
}

Compiler comme ceci pour générer le désassemblage

go build -gcflags -S iter.go

Voici la plaine (j'ai supprimé les non instructions de la liste)

installer

0035 (/home/ncw/Go/iter.go:14) MOVQ    $0,AX
0036 (/home/ncw/Go/iter.go:14) JMP     ,38

loop

0037 (/home/ncw/Go/iter.go:14) INCQ    ,AX
0038 (/home/ncw/Go/iter.go:14) CMPQ    AX,$10
0039 (/home/ncw/Go/iter.go:14) JGE     $0,45
0040 (/home/ncw/Go/iter.go:15) MOVQ    AX,i+-8(SP)
0041 (/home/ncw/Go/iter.go:15) MOVQ    AX,(SP)
0042 (/home/ncw/Go/iter.go:15) CALL    ,p+0(SB)
0043 (/home/ncw/Go/iter.go:15) MOVQ    i+-8(SP),AX
0044 (/home/ncw/Go/iter.go:14) JMP     ,37
0045 (/home/ncw/Go/iter.go:17) RET     ,

Et voici with_iter

installer

0052 (/home/ncw/Go/iter.go:20) MOVQ    $10,AX
0053 (/home/ncw/Go/iter.go:20) MOVQ    $0,~r0+-24(SP)
0054 (/home/ncw/Go/iter.go:20) MOVQ    $0,~r0+-16(SP)
0055 (/home/ncw/Go/iter.go:20) MOVQ    $0,~r0+-8(SP)
0056 (/home/ncw/Go/iter.go:20) MOVQ    $type.[]struct {}+0(SB),(SP)
0057 (/home/ncw/Go/iter.go:20) MOVQ    AX,8(SP)
0058 (/home/ncw/Go/iter.go:20) MOVQ    AX,16(SP)
0059 (/home/ncw/Go/iter.go:20) PCDATA  $0,$48
0060 (/home/ncw/Go/iter.go:20) CALL    ,runtime.makeslice+0(SB)
0061 (/home/ncw/Go/iter.go:20) PCDATA  $0,$-1
0062 (/home/ncw/Go/iter.go:20) MOVQ    24(SP),DX
0063 (/home/ncw/Go/iter.go:20) MOVQ    32(SP),CX
0064 (/home/ncw/Go/iter.go:20) MOVQ    40(SP),AX
0065 (/home/ncw/Go/iter.go:20) MOVQ    DX,~r0+-24(SP)
0066 (/home/ncw/Go/iter.go:20) MOVQ    CX,~r0+-16(SP)
0067 (/home/ncw/Go/iter.go:20) MOVQ    AX,~r0+-8(SP)
0068 (/home/ncw/Go/iter.go:20) MOVQ    $0,AX
0069 (/home/ncw/Go/iter.go:20) LEAQ    ~r0+-24(SP),BX
0070 (/home/ncw/Go/iter.go:20) MOVQ    8(BX),BP
0071 (/home/ncw/Go/iter.go:20) MOVQ    BP,autotmp_0006+-32(SP)
0072 (/home/ncw/Go/iter.go:20) JMP     ,74

loop

0073 (/home/ncw/Go/iter.go:20) INCQ    ,AX
0074 (/home/ncw/Go/iter.go:20) MOVQ    autotmp_0006+-32(SP),BP
0075 (/home/ncw/Go/iter.go:20) CMPQ    AX,BP
0076 (/home/ncw/Go/iter.go:20) JGE     $0,82
0077 (/home/ncw/Go/iter.go:20) MOVQ    AX,autotmp_0005+-40(SP)
0078 (/home/ncw/Go/iter.go:21) MOVQ    AX,(SP)
0079 (/home/ncw/Go/iter.go:21) CALL    ,p+0(SB)
0080 (/home/ncw/Go/iter.go:21) MOVQ    autotmp_0005+-40(SP),AX
0081 (/home/ncw/Go/iter.go:20) JMP     ,73
0082 (/home/ncw/Go/iter.go:23) RET     ,

Ainsi, vous pouvez voir que la solution iter est considérablement plus chère même si elle est totalement intégrée dans la phase d’installation. Dans la phase de boucle, il y a une instruction supplémentaire dans la boucle, mais ce n'est pas si mal.

Je voudrais utiliser le simple pour la boucle.

33
Nick Craig-Wood

Mark Mishyn a suggéré d'utiliser slice, mais il n'y a aucune raison de créer un tableau avec make et de l'utiliser dans for a renvoyé une tranche lorsque ce dernier a été créé et qu'il est plus court

for i := range [5]int{} {
        fmt.Println(i)
}
15
Daniil Grankin

iter est un très petit paquet qui fournit simplement une manière différente de parcourir des nombres entiers.

for i := range iter.N(4) {
    fmt.Println(i)
}

Rob Pike (un auteur de Go) l'a critiqué :

Il semble que presque chaque fois que quelqu'un trouve un moyen d'éviter faire quelque chose comme une boucle for idiomatique, parce que ça sent trop long ou fastidieux, le résultat est presque toujours plus de frappes que la chose qui est soi-disant plus courte. [...] Cela laisse de côté toutes les dépenses folles que ces "améliorations" apportent.

12
elithrar

Voici un point de repère pour comparer une instruction Go for avec une instruction ForClause et une instruction Go range à l'aide du package iter.

iter_test.go

package main

import (
    "testing"

    "github.com/bradfitz/iter"
)

const loops = 1e6

func BenchmarkForClause(b *testing.B) {
    b.ReportAllocs()
    j := 0
    for i := 0; i < b.N; i++ {
        for j = 0; j < loops; j++ {
            j = j
        }
    }
    _ = j
}

func BenchmarkRangeIter(b *testing.B) {
    b.ReportAllocs()
    j := 0
    for i := 0; i < b.N; i++ {
        for j = range iter.N(loops) {
            j = j
        }
    }
    _ = j
}

// It does not cause any allocations.
func N(n int) []struct{} {
    return make([]struct{}, n)
}

func BenchmarkIterAllocs(b *testing.B) {
    b.ReportAllocs()
    var n []struct{}
    for i := 0; i < b.N; i++ {
        n = iter.N(loops)
    }
    _ = n
}

Sortie:

$ go test -bench=. -run=.
testing: warning: no tests to run
PASS
BenchmarkForClause      2000       1260356 ns/op           0 B/op          0 allocs/op
BenchmarkRangeIter      2000       1257312 ns/op           0 B/op          0 allocs/op
BenchmarkIterAllocs 20000000            82.2 ns/op         0 B/op          0 allocs/op
ok      so/test 7.026s
$
8
peterSO

Bien que je comprenne votre inquiétude à propos de l'absence de cette fonctionnalité de langage, vous allez probablement vouloir utiliser une boucle for normale. Et cela vous conviendra probablement mieux que vous ne le pensez en écrivant plus de code Go.

J'ai écrit this iter package - qui s'appuie sur une simple boucle idiomatique for qui renvoie les valeurs sur un chan int - dans le but d'améliorer la conception trouvée dans https://github.com/bradfitz/iter , qui a été signalé comme ayant des problèmes de mise en cache et de performances, ainsi que par une implémentation astucieuse, mais étrange et peu intuitive. Ma propre version fonctionne de la même manière:

package main

import (
    "fmt"
    "github.com/drgrib/iter"
)

func main() {
    for i := range iter.N(10) {
        fmt.Println(i)
    }
}

Toutefois, l’analyse comparative a révélé que l’utilisation d’un canal était une option très coûteuse. La comparaison des 3 méthodes pouvant être exécutées à partir de iter_test.go dans mon paquet en utilisant

go test -bench=. -run=.

quantifie à quel point ses performances sont médiocres

BenchmarkForMany-4                   5000       329956 ns/op           0 B/op          0 allocs/op
BenchmarkDrgribIterMany-4               5    229904527 ns/op         195 B/op          1 allocs/op
BenchmarkBradfitzIterMany-4          5000       337952 ns/op           0 B/op          0 allocs/op

BenchmarkFor10-4                500000000         3.27 ns/op           0 B/op          0 allocs/op
BenchmarkDrgribIter10-4            500000      2907 ns/op             96 B/op          1 allocs/op
BenchmarkBradfitzIter10-4       100000000        12.1 ns/op            0 B/op          0 allocs/op

Au cours du processus, ce test de performance montre également comment la solution bradfitz sous-exécute par rapport à la clause for intégrée pour une taille de boucle de 10.

En résumé, il semble jusqu'à présent impossible de reproduire les performances de la clause for intégrée tout en fournissant une syntaxe simple pour [0,n) comme celle trouvée dans Python et Ruby.

Ce qui est dommage car il serait probablement facile pour l’équipe Go d’ajouter une règle simple au compilateur pour modifier une ligne comme celle-ci.

for i := range 10 {
    fmt.Println(i)
}

au même code machine que for i := 0; i < 10; i++.

Cependant, pour être juste, après avoir écrit mon propre iter.N (mais avant de le comparer), je suis revenu dans un programme récemment écrit pour voir tous les endroits où je pouvais l'utiliser. En réalité, ils n'étaient pas nombreux. Il n'y avait qu'un seul endroit, dans une section non essentielle de mon code, où je pouvais me débrouiller sans la clause for par défaut plus complète.

Ainsi, même si cela peut paraître décevant en principe pour la formulation, vous constaterez peut-être - comme je l’ai fait auparavant - que vous n’en avez pas vraiment besoin dans la pratique. Comme Rob Pike est connu pour ses génériques, vous ne manquerez peut-être pas cette fonctionnalité autant que vous le pensez.

3
Chris Redford
package main

import "fmt"

func main() {

    nums := []int{2, 3, 4}
    for _, num := range nums {
       fmt.Println(num, sum)    
    }
}
1
Dvv Avinash

Si vous souhaitez simplement parcourir une plage sans utiliser ni index ni quoi que ce soit d'autre, cet exemple de code a parfaitement fonctionné pour moi. Aucune déclaration supplémentaire nécessaire, pas de _. Je n'ai pas vérifié la performance, cependant.

for range [N]int{} {
    // Body...
}

P.S. Le tout premier jour à GoLang. S'il vous plaît, faites une critique si c'est une mauvaise approche.

1
WHS

Vous pouvez également consulter github.com/wushilin/stream

C'est un flux lazy comme concept de Java.util.stream.

import "github.com/wushilin/stream"
stream1 := stream.Range(0, 10) // it doesn't really allocate the 
// 10   elements
stream1.Each(print) // => print each element
stream2 := stream1.Map(func(i int) int) {
  return i + 3
}) // Add 3 to each element, but it is a lazy add. 
// You only add when consume the stream
stream2.Reduce(func(i, j int) int {
  return i + j
 }) // well, this consumes the stream => return sum of stream2

stream3 := stream.Of(1, 2,3,4,5) // create stream with 5 elements
stream4 := stream.FromArray(arrayInput) // create stream from array
stream3.Filter(func(i int) bool {
  return i > 2
}).Sum() => Filter stream3, keep only elements that is bigger than 
//2, and return the Sum, which is 12

J'espère que cela t'aides

0
Wu Shilin