Go의 범위는 맵과 슬라이스를 반복 할 수 있지만 다음과 같은 범위의 숫자를 반복하는 방법이 있는지 궁금합니다.
for i := range [1..10] {
fmt.Println(i)
}
또는 Ruby가 Range 클래스를 사용하는 방식과 같이 Go에서 정수 범위를 나타내는 방법이 있습니까?
Go의 범위는 맵과 슬라이스를 반복 할 수 있지만 다음과 같은 범위의 숫자를 반복하는 방법이 있는지 궁금합니다.
for i := range [1..10] {
fmt.Println(i)
}
또는 Ruby가 Range 클래스를 사용하는 방식과 같이 Go에서 정수 범위를 나타내는 방법이 있습니까?
답변:
for 루프를 작성할 수 있습니다. 간단하고 명백한 코드는 Go Way입니다.
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
지금까지 제안 된 두 가지 방법을 비교하는 프로그램이 있습니다.
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 루프를 사용합니다.
runtime.makeslice
되고 다른 솔루션은 그렇지 않습니다. 훨씬 느리다는 것을 알기 위해 벤치 마크가 필요하지 않습니다!
runtime.makeslice
크기를 0으로 할당하면 메모리를 할당 할 수 없을만큼 영리합니다. 그러나 위의 내용은 여전히 전화이며 벤치 마크에 따르면 내 컴퓨터에서 10nS가 더 오래 걸립니다.
Mark Mishyn은 slice를 사용하도록 제안했지만 리터럴을 통해 생성 된 배열을 사용할 수 있고 더 짧을 때 배열을 make
사용하여 for
반환 된 슬라이스 에서 사용할 이유가 없습니다.
for i := range [5]int{} {
fmt.Println(i)
}
for range [5]int{} {
5
여기에 리터럴이며 런타임에 확인할 수 없다는 것입니다.
iter 는 매우 작은 패키지로 정수를 반복하는 구문 적으로 다른 방법을 제공합니다.
for i := range iter.N(4) {
fmt.Println(i)
}
Rob Pike (Go의 저자) 는이를 비판했습니다 .
너무 길거나 성가신 느낌이 들기 때문에 누군가가 for 루프와 같은 관용적 인 방법을 피하는 방법을 생각해 낼 때마다 거의 짧은 키보다 키 스트로크가 더 많이 발생합니다. [...] 그것은이 "개선"이 가져 오는 모든 미친 오버 헤드를 제쳐두고 있습니다.
iter
하기 때문에 버전은 실제로 더 적은 키 입력을 사용 range
하고 iter
자동으로 완성됩니다.
for
루프는 유닉스의 일류 시민이 아닙니다. 게다가, 달리 for
, seq
표준 출력에 숫자의 순서를 스트림. 반복 여부는 소비자에게 달려 있습니다. for i in $(seq 1 10); do ... done
Shell에서 일반적 이지만 for 루프를 수행하는 한 가지 방법 일뿐입니다.이 루프 자체는 seq
비록 매우 일반적인 방법이지만 의 출력을 소비하는 유일한 방법 입니다.
i in range(10)
정확히 취급하는 방식으로 빌드 할 수 있다는 사실을 고려하지 않습니다 i := 0; i < 10; i++
.
다음 은 패키지를 사용하여 for
Go 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
$
이 언어 기능이 부족하다는 걱정을하면서 일반 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가 제네릭에 대해 말하는 것으로 알려진 것처럼 실제로이 기능을 생각만큼 그리워하지 않을 수도 있습니다.
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()
도움이 되었기를 바랍니다
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
for _, num := range nums {
fmt.Println(num, sum)
}
}
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, 때로는 도움이 될 수 있습니다