정수 범위를 반복하는 방법이 있습니까?


174

Go의 범위는 맵과 슬라이스를 반복 할 수 있지만 다음과 같은 범위의 숫자를 반복하는 방법이 있는지 궁금합니다.

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

또는 Ruby가 Range 클래스를 사용하는 방식과 같이 Go에서 정수 범위를 나타내는 방법이 있습니까?

답변:


224

for 루프를 작성할 수 있습니다. 간단하고 명백한 코드는 Go Way입니다.

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

268
나는 대부분의 사람들이 @Vishnu가 쓴 것 보다이 3 표현 버전을 더 단순하다고 생각하지 않습니다. 몇 년 또는 몇 년에 걸친 C 또는 Java 교리 후 ;-)
Thomas Ahle

12
IMO의 요점은 항상이 세 가지 표현 버전의 for 루프를 사용한다는 것입니다. 즉, 더 많은 작업을 수행 할 수 있습니다. OP의 구문은 숫자 범위가 제한된 경우에만 적합합니다. 모든 언어 에서이 확장 버전을 원할 것입니다)) 동일한 작업을 충분히 수행하고 어쨌든 크게 다르지 않으므로 다른 구문을 배우거나 기억 해야하는 이유는 무엇입니까? 크고 복잡한 프로젝트에서 코딩하는 경우 루프처럼 간단한 구문에 대해 다양한 구문에 대해 컴파일러와 싸울 필요없이 이미 걱정할 필요가 있습니다.
Brad Peabody

3
@ThomasAhle 특히 공식적으로 부스트 템플릿 라이브러리에서 영감을 표기의 for_each (X, Y)를 추가하고 C ++을 고려
밝은 돈

5
@BradPeabody 이것은 실제로 선호의 문제입니다. 파이썬에는 3 식 루프가 없으며 정상적으로 작동합니다. 많은 사람들이 for-each 구문을 오류가 덜 발생한다고 생각하고 본질적으로 비효율적 인 것은 없습니다.
VinGarcia 2012 년

3
@necromancer는 Rob Pike의 게시물로 내 대답과 거의 동일한 내용을 주장합니다. groups.google.com/d/msg/golang-nuts/7J8FY07dkW0/goWaNVOkQU0J . Go 커뮤니티가 동의하지 않을 수도 있지만 언어 작성자 중 하나와 동의 할 때 실제로 대답이 나쁘지는 않습니다.
Paul Hankin

43

지금까지 제안 된 두 가지 방법을 비교하는 프로그램이 있습니다.

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

이와 같이 컴파일하여 디스 어셈블리를 생성하십시오.

go build -gcflags -S iter.go

여기는 평범합니다 (목록에서 비 지시 사항을 제거했습니다)

설정

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

고리

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     ,

그리고 여기 with_iter가 있습니다

설정

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

고리

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     ,

따라서 iter 솔루션이 설정 단계에서 완전히 인라인되어 있어도 훨씬 더 비싼 것을 알 수 있습니다. 루프 단계에는 루프에 추가 명령어가 있지만 그렇게 나쁘지는 않습니다.

간단한 for 루프를 사용합니다.


8
"Iter 솔루션이 훨씬 더 비싸다는 것을 알 수 없습니다." Go 의사 어셈블리 명령어 계산 방법에 결함이 있습니다. 벤치 마크를 실행하십시오.
peterSO

11
하나의 솔루션이 호출 runtime.makeslice되고 다른 솔루션은 그렇지 않습니다. 훨씬 느리다는 것을 알기 위해 벤치 마크가 필요하지 않습니다!
Nick Craig-Wood

6
예, runtime.makeslice크기를 0으로 할당하면 메모리를 할당 할 수 없을만큼 영리합니다. 그러나 위의 내용은 여전히 ​​전화이며 벤치 마크에 따르면 내 컴퓨터에서 10nS가 더 오래 걸립니다.
Nick Craig-Wood

4
이것은 성능상의 이유로 C ++보다 C ++를 사용하라고 제안하는 사람들을 상기시킵니다.
necromancer

5
Goland에서 일반적이지만 나노초 CPU 작업의 런타임 성능을 논의하는 것은 어리석은 것처럼 보입니다. 나는 가독성 후에 매우 먼 마지막 고려 사항이라고 생각합니다. CPU 성능이 관련되어 있어도 for 루프의 내용은 거의 항상 루프 자체에 의해 발생하는 차이점을 습득합니다.
Jonathan Hartley

34

Mark Mishyn은 slice를 사용하도록 제안했지만 리터럴을 통해 생성 된 배열을 사용할 수 있고 더 짧을 때 배열을 make사용하여 for반환 된 슬라이스 에서 사용할 이유가 없습니다.

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

8
변수를 사용하지 않으려면 왼쪽을 생략하고 다음을 사용할 수도 있습니다.for range [5]int{} {
blockloop

6
단점은 5여기에 리터럴이며 런타임에 확인할 수 없다는 것입니다.
Steve Powell

루프에 대한 일반적인 세 ​​가지 표현식과 비교하여 더 빠르거나 비슷한가요?
Amit Tripathi

@AmitTripathi 예, 비교할 수 있습니다. 실행 시간은 수십억 번의 반복에서 거의 동일합니다.
Daniil Grankin

18

iter 는 매우 작은 패키지로 정수를 반복하는 구문 적으로 다른 방법을 제공합니다.

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

Rob Pike (Go의 저자) 는이를 비판했습니다 .

너무 길거나 성가신 느낌이 들기 때문에 누군가가 for 루프와 같은 관용적 인 방법을 피하는 방법을 생각해 낼 때마다 거의 짧은 키보다 키 스트로크가 더 많이 발생합니다. [...] 그것은이 "개선"이 가져 오는 모든 미친 오버 헤드를 제쳐두고 있습니다.


16
파이크의 비판은 범위를 지속적으로 재정의하는 정신적 오버 헤드보다는 키 스트로크 만 해결한다는 점에서 단순합니다. 또한, 대부분의 에디터들과 함께 iter하기 때문에 버전은 실제로 더 적은 키 입력을 사용 range하고 iter자동으로 완성됩니다.
Chris Redford

1
@ lang2, for루프는 유닉스의 일류 시민이 아닙니다. 게다가, 달리 for, seq표준 출력에 숫자의 순서를 스트림. 반복 여부는 소비자에게 달려 있습니다. for i in $(seq 1 10); do ... done Shell에서 일반적 이지만 for 루프를 수행하는 한 가지 방법 일뿐입니다.이 루프 자체는 seq비록 매우 일반적인 방법이지만 의 출력을 소비하는 유일한 방법 입니다.
다니엘 파렐

2
또한 파이크는 컴파일 (언어 사양 에이 사용 사례에 대한 범위 구문이 포함되어 있음)을 i in range(10)정확히 취급하는 방식으로 빌드 할 수 있다는 사실을 고려하지 않습니다 i := 0; i < 10; i++.
Rouven B.

8

다음 은 패키지를 사용하여 forGo range문과 ForClause 및 Go 문 을 비교하는 벤치 마크 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
}

산출:

$ 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
$

5
루프를 10으로 설정 한 후 벤치 마크를 다시 시도하면 뚜렷한 차이가 나타납니다. 내 컴퓨터에서 ForClause는 5.6ns가 걸리고 Iter는 15.4ns가 걸리므로 할당자를 호출하는 데 (아무것도 할당 할만 큼 영리하지는 않지만) 할당하는 데 여전히 10ns가 걸리고 I- 캐시 버스 팅 코드가 많이 필요합니다.
Nick Craig-Wood

내 답변에서 만들고 참조한 패키지에 대한 벤치 마크와 비평을보고 싶습니다 .
Chris Redford

5

이 언어 기능이 부족하다는 걱정을하면서 일반 for루프 를 사용하고 싶을 것 입니다. 그리고 더 많은 Go 코드를 작성할 때 생각하는 것보다 더 좋을 것입니다.

https://github.com/bradfitz/iter 에서 발견 된 디자인을 개선하기 위해이 iter 패키지를 작성 했습니다. 단순하고 관용적 인 for루프로 뒷받침됩니다. 캐싱 및 성능 문제, 영리하지만 이상하고 직관적이지 않은 구현. 내 자신의 버전은 같은 방식으로 작동합니다.chan int

package main

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

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

그러나 벤치마킹 결과 채널 사용이 매우 비싼 옵션이라는 것이 밝혀졌습니다. iter_test.go내 패키지에서 실행할 수있는 3 가지 방법의 비교

go test -bench=. -run=.

성능이 얼마나 나쁜지 정량화

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

이 과정에서이 벤치 마크는 루프 크기가 bradfitz내장 된 for절 과 비교 하여 솔루션의 성능이 어떻게 저하 되는지 보여줍니다 10.

간단히 말해서 지금까지 내장 for절의 성능을 복제 하면서 [0,n)파이썬과 루비에서 발견되는 것과 유사한 간단한 구문을 제공하는 방법은 발견되지 않은 것 같습니다.

Go 팀이 간단한 규칙을 컴파일러에 추가하여 다음과 같은 행을 변경하기가 쉽기 때문에 부끄러운 일입니다.

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

와 동일한 머신 코드에 for i := 0; i < 10; i++.

그러나 공정하게 말하면, 내 자신의 글을 작성한 후 iter.N(그러나 벤치마킹하기 전에), 최근에 작성된 프로그램을 통해 내가 사용할 수있는 모든 장소를 보았습니다. 실제로는 많지 않았습니다. 내 코드의 중요하지 않은 섹션에는 더 완벽한 기본 for절 없이 얻을 수있는 지점이 하나뿐이었습니다 .

따라서 이것이 원칙적으로 언어에 크게 실망한 것처럼 보이지만 실제로는 실제로는 실제로 필요하지 않은 것을 알 수 있습니다. Rob Pike가 제네릭에 대해 말하는 것으로 알려진 것처럼 실제로이 기능을 생각만큼 그리워하지 않을 수도 있습니다.


1
반복에 채널을 사용하는 것은 매우 비쌉니다. 고 루틴과 채널은 싸고 무료는 아닙니다. 채널에 대한 반복 범위가 조기에 종료되면 고 루틴이 끝나지 않습니다 (고 루틴 누출). Iter 메소드가 벡터 패키지 에서 삭제되었습니다 . " 컨테이너 / 벡터 : 인터페이스에서 Iter ()를 제거합니다 (Iter ()는 호출하기에 적합한 메커니즘 이 아닙니다 ). " iter 솔루션은 항상 가장 비쌉니다.
peterSO

4

사용하거나 인덱스 또는 다른 것을 사용하지 않고 범위를 반복하려면이 코드 샘플이 저에게 잘 작동했습니다. 추가 선언이 필요하지 않습니다 _. 그래도 성능을 확인하지 않았습니다.

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

PS GoLang의 첫날. 잘못된 접근 방식이라면 비판하십시오.


지금까지 (버전 1.13.6)에서는 작동하지 않습니다. non-constant array bound날 던지고
WHS

1

github.com/wushilin/stream을 확인할 수도 있습니다

java.util.stream의 개념과 같은 게으른 스트림입니다.

// It doesn't really allocate the 10 elements.
stream1 := stream.Range(0, 10)

// Print each element.
stream1.Each(print)

// Add 3 to each element, but it is a lazy add.
// You only add when consume the stream
stream2 := stream1.Map(func(i int) int {
    return i + 3
})

// Well, this consumes the stream => return sum of stream2.
stream2.Reduce(func(i, j int) int {
    return i + j
})

// Create stream with 5 elements
stream3 := stream.Of(1, 2, 3, 4, 5)

// Create stream from array
stream4 := stream.FromArray(arrayInput)

// Filter stream3, keep only elements that is bigger than 2,
// and return the Sum, which is 12
stream3.Filter(func(i int) bool {
    return i > 2
}).Sum()

도움이 되었기를 바랍니다


0
package main

import "fmt"

func main() {

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

1
미래 독자들이 그 의미를 더 잘 이해할 수 있도록 코드에 컨텍스트를 추가하십시오.
그랜트 밀러

3
이게 뭐야? 합계가 정의되지 않았습니다.
naftalimich

0

Golang에서 파이썬의 범위 함수를 모방 한 패키지를 작성했습니다.

패키지 https://github.com/thedevsaddam/iter

package main

import (
    "fmt"

    "github.com/thedevsaddam/iter"
)

func main() {
    // sequence: 0-9
    for v := range iter.N(10) {
        fmt.Printf("%d ", v)
    }
    fmt.Println()
    // output: 0 1 2 3 4 5 6 7 8 9

    // sequence: 5-9
    for v := range iter.N(5, 10) {
        fmt.Printf("%d ", v)
    }
    fmt.Println()
    // output: 5 6 7 8 9

    // sequence: 1-9, increment by 2
    for v := range iter.N(5, 10, 2) {
        fmt.Printf("%d ", v)
    }
    fmt.Println()
    // output: 5 7 9

    // sequence: a-e
    for v := range iter.L('a', 'e') {
        fmt.Printf("%s ", string(v))
    }
    fmt.Println()
    // output: a b c d e
}

참고 : 나는 재미를 위해 작성했습니다! Btw, 때로는 도움이 될 수 있습니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.