webentwicklung-frage-antwort-db.com.de

Gibt es eine Möglichkeit, eine Reihe von ganzen Zahlen in Golang zu durchlaufen?

Golangs Bereich kann über Karten und Segmente iterieren, aber ich habe mich gefragt, ob es eine Möglichkeit gibt, eine Reihe von Zahlen zu durchlaufen

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

oder gibt es eine Möglichkeit, eine ganze Reihe von Ganzzahlen in Go darzustellen, wie dies bei Ruby der Fall ist?

118
Vishnu

Sie können und sollten einfach eine for-Schleife schreiben. Einfacher, offensichtlicher Code ist der Go-Weg.

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

Hier ist ein Programm zum Vergleich der zwei bisher vorgeschlagenen Möglichkeiten

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()
}

So kompilieren, dass eine Demontage erfolgt

go build -gcflags -S iter.go

Hier ist einfach (ich habe die Nicht-Anweisungen aus der Liste entfernt)

konfiguration

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     ,

Und hier ist with_iter

konfiguration

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     ,

Sie sehen also, dass die Iter-Lösung erheblich teurer ist, obwohl sie in der Setup-Phase vollständig integriert ist. In der Schleifenphase befindet sich eine zusätzliche Anweisung in der Schleife, die jedoch nicht zu schlecht ist.

Ich würde die einfache for-Schleife verwenden.

33
Nick Craig-Wood

Mark Mishyn hat die Verwendung von slice vorgeschlagen. Es besteht jedoch kein Grund, ein Array mit make zu erstellen und das in for zurückgegebene Slice davon zu verwenden, wenn das über Literal erstellte Array verwendet werden kann und kürzer ist

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

iter ist ein sehr kleines Paket, das nur eine syntantisch andere Möglichkeit zum Durchlaufen von ganzen Zahlen bietet.

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

Rob Pike (ein Autor von Go) hat es kritisiert :

Es scheint, dass fast jedes Mal, wenn jemand einen Ausweg findet, __. etwas wie eine for-Schleife auf die idiomatische Weise zu tun, weil es sich anfühlt zu lang oder umständlich ist das Ergebnis fast immer mehr Tastatureingaben als das Ding, das angeblich kürzer ist. [...] Abgesehen von all dem verrückten Aufwand, den diese "Verbesserungen" bringen.

12
elithrar

Hier ist ein Benchmark, um eine Go for-Anweisung mit einer ForClause- und einer Go range-Anweisung mit dem iter-Paket zu vergleichen.

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
}

Ausgabe:

$ 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

Während ich mich mit Ihrer Besorgnis darüber aussehe, dass Sie diese Sprachfunktion nicht haben, möchten Sie wahrscheinlich nur eine normale for-Schleife verwenden. Und Sie werden wahrscheinlich damit einverstanden sein, als Sie denken, wenn Sie mehr Go-Code schreiben.

Ich habe dieses Iterer-Paket - geschrieben, das durch eine einfache, idiomatische for-Schleife unterstützt wird, die Werte über einen chan int zurückgibt, um das in https://github.com/bradfitz/iter gefundene Design zu verbessern , auf das Caching- und Performance-Probleme sowie eine clevere, aber merkwürdige und unintuitive Implementierung hingewiesen wurden. Meine eigene Version funktioniert genauso:

package main

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

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

Das Benchmarking ergab jedoch, dass die Verwendung eines Channels eine sehr teure Option war. Der Vergleich der 3 Methoden, die von iter_test.go in meinem Paket mit ausgeführt werden können

go test -bench=. -run=.

quantifiziert, wie schlecht seine Leistung ist

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

In diesem Prozess zeigt dieser Benchmark auch, wie die bradfitz-Lösung im Vergleich zur eingebauten for-Klausel für eine Schleifengröße von 10 unterdurchschnittlich ist.

Kurz gesagt scheint es bisher keine Möglichkeit zu geben, die Leistung der integrierten for-Klausel zu duplizieren, während eine einfache Syntax für [0,n) bereitgestellt wird, wie sie in Python und Ruby zu finden ist.

Was sehr schade ist, denn es wäre für das Go-Team wahrscheinlich einfach, dem Compiler eine einfache Regel hinzuzufügen, um eine Zeile wie diese zu ändern

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

auf denselben Maschinencode wie for i := 0; i < 10; i++.

Um fair zu sein, habe ich, nachdem ich meinen eigenen iter.N geschrieben hatte (aber vor dem Benchmarking), ein kürzlich geschriebenes Programm durchgesehen, um alle Orte zu sehen, an denen ich es verwenden konnte. Es gab eigentlich nicht viele. Es gab nur eine Stelle in einem nicht lebenswichtigen Abschnitt meines Codes, an der ich ohne die vollständigere Standardklausel for auskommen konnte.

Obwohl es so aussieht, als wäre dies eine große Enttäuschung für die Sprache im Prinzip, können Sie - wie ich - feststellen, dass Sie sie in der Praxis eigentlich nicht wirklich brauchen. Wie Rob Pike für Generics bekannt ist, werden Sie diese Funktion möglicherweise nicht so sehr vermissen, wie Sie glauben.

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

Wenn Sie nur über einen Bereich ohne Verwendung von Indizes oder etwas anderem iterieren möchten, hat dieses Codebeispiel für mich einwandfrei funktioniert. Keine zusätzliche Deklaration erforderlich, kein _. Die Leistung wurde jedoch nicht überprüft.

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

P.S. Der allererste Tag in GoLang. Bitte kritisieren Sie, wenn es ein falscher Ansatz ist.

1
WHS

Sie können auch nachsehengithub.com/wushilin/stream

Es ist ein fauler Strom wie das Konzept von 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

Hoffe das hilft

0
Wu Shilin