ช่วงของ Go สามารถทำซ้ำในแผนที่และส่วนต่าง ๆ แต่ฉันสงสัยว่าถ้ามีวิธีที่จะวนซ้ำในช่วงของตัวเลขบางอย่างเช่นนี้:
for i := range [1..10] {
fmt.Println(i)
}
หรือมีวิธีที่จะแสดงช่วงของจำนวนเต็มใน Go เหมือนกับที่ Ruby ทำกับRange Classอย่างไร?
ช่วงของ Go สามารถทำซ้ำในแผนที่และส่วนต่าง ๆ แต่ฉันสงสัยว่าถ้ามีวิธีที่จะวนซ้ำในช่วงของตัวเลขบางอย่างเช่นนี้:
for i := range [1..10] {
fmt.Println(i)
}
หรือมีวิธีที่จะแสดงช่วงของจำนวนเต็มใน Go เหมือนกับที่ Ruby ทำกับRange Classอย่างไร?
คำตอบ:
คุณสามารถและควรเพียงแค่เขียนสำหรับวง รหัสที่ง่ายและชัดเจนคือวิธีการใช้งาน
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 มีราคาแพงกว่ามากแม้ว่าจะอยู่ในช่วงการติดตั้งอย่างสมบูรณ์ก็ตาม ในเฟสลูปมีคำสั่งพิเศษในลูป แต่ก็ไม่ได้แย่เกินไป
ฉันจะใช้ง่ายสำหรับวน
runtime.makeslice
และวิธีอื่นไม่ได้ - ฉันไม่ต้องการมาตรฐานเพื่อทราบว่าจะช้ากว่านี้มาก!
runtime.makeslice
ฉลาดพอที่จะไม่จัดสรรหน่วยความจำใด ๆ หากคุณขอการจัดสรรขนาดเป็นศูนย์ อย่างไรก็ตามข้างต้นยังคงเรียกมันและตามมาตรฐานของคุณจะใช้เวลา 10nS บนเครื่องของฉันอีกต่อไป
แนะนำโดย Mark Mishyn ให้ใช้การแบ่งเป็นชิ้น แต่ไม่มีเหตุผลที่จะสร้าง array ด้วยmake
และใช้ในfor
slice ที่ส่งคืนของมันเมื่อ array ที่สร้างผ่านตัวอักษรสามารถใช้งานได้และสั้นกว่า
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) ได้วิจารณ์ว่า :
ดูเหมือนว่าเกือบทุกครั้งที่มีคนหาวิธีหลีกเลี่ยงการทำสิ่งใดสิ่งหนึ่งเพื่อทำวนสำนวนเพราะรู้สึกยาวเกินไปหรือยุ่งยากผลลัพธ์ก็คือการกดแป้นมากกว่าปกติที่สั้นกว่าที่ควรจะเป็น [... ] นั่นคือการละทิ้งความบ้าคลั่งทั้งหมดที่ "การปรับปรุง" นำมาให้
iter
เวอร์ชันจะใช้การกดแป้นพิมพ์น้อยลงเพราะrange
และiter
จะเติมข้อความอัตโนมัติ
for
ลูปไม่ได้เป็นพลเมืองอันดับหนึ่งของ Unix เหมือนพวกเขาอยู่ในระหว่างการเดินทาง นอกจากนี้ไม่เหมือนfor
, seq
ลำธารออกมาตรฐานลำดับของตัวเลข ขึ้นอยู่กับผู้บริโภคหรือไม่ แม้ว่าจะfor i in $(seq 1 10); do ... done
เป็นเรื่องปกติในเชลล์ แต่ก็มีวิธีเดียวที่จะทำเพื่อลูปซึ่งเป็นวิธีเดียวที่จะใช้เอาต์พุตของseq
แม้ว่าจะเป็นวิธีที่ใช้กันทั่วไป
i in range(10)
i := 0; i < 10; i++
นี่คือมาตรฐานในการเปรียบเทียบfor
คำสั่งGo กับคำสั่ง ForClause และ Go range
โดยใช้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 เพิ่มเติม
ฉันเขียนแพ็คเกจ iter นี้ - ซึ่งได้รับการสนับสนุนโดยfor
ลูปที่เรียบง่ายและเป็นสำนวนที่คืนค่ามากกว่าchan int
- ในความพยายามที่จะปรับปรุงการออกแบบที่พบในhttps://github.com/bradfitz/iterซึ่งได้รับการชี้ให้เห็นว่ามี ปัญหาการแคชและประสิทธิภาพรวมถึงการใช้งานที่ฉลาด แต่แปลกและไม่เข้าใจง่าย รุ่นของฉันทำงานด้วยวิธีเดียวกัน:
package main
import (
"fmt"
"github.com/drgrib/iter"
)
func main() {
for i := range iter.N(10) {
fmt.Println(i)
}
}
อย่างไรก็ตามการเปรียบเทียบพบว่าการใช้ช่องเป็นตัวเลือกที่แพงมาก การเปรียบเทียบ 3 วิธีซึ่งสามารถเรียกใช้จากiter_test.go
ในแพ็คเกจของฉันโดยใช้
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)
เช่นเดียวกับที่พบใน Python และ Ruby
ซึ่งเป็นความอัปยศเพราะอาจเป็นเรื่องง่ายสำหรับทีม Go ที่จะเพิ่มกฎง่ายๆให้กับคอมไพเลอร์เพื่อเปลี่ยนบรรทัดเช่น
for i := range 10 {
fmt.Println(i)
}
for i := 0; i < 10; i++
ไปที่รหัสเครื่องเดียวกับ
อย่างไรก็ตามเพื่อความเป็นธรรมหลังจากเขียนของฉันเองiter.N
(แต่ก่อนที่จะทำการเปรียบเทียบ) ฉันกลับไปที่โปรแกรมที่เขียนเมื่อเร็ว ๆ นี้เพื่อดูสถานที่ทั้งหมดที่ฉันสามารถใช้ได้ จริงๆแล้วมีไม่มาก มีเพียงจุดเดียวในส่วนที่ไม่สำคัญของรหัสของฉันซึ่งฉันสามารถทำได้โดยไม่ต้องใช้for
ประโยคเริ่มต้นที่สมบูรณ์มากขึ้น
ดังนั้นในขณะที่อาจดูเหมือนว่านี่เป็นความผิดหวังอย่างมากสำหรับภาษาในหลักการคุณอาจพบว่า - เหมือนที่ฉันทำ - ที่จริงแล้วคุณไม่ต้องการใช้มัน เช่นเดียวกับที่ Rob Pike รู้จักกันในชื่อ generics คุณอาจไม่พลาดฟีเจอร์นี้มากเท่าที่คุณคิด
หากคุณต้องการย้ำช่วงที่ไม่มีการใช้และดัชนีหรืออย่างอื่นตัวอย่างรหัสนี้ใช้ได้ดีสำหรับฉัน _
ไม่มีประกาศพิเศษที่จำเป็นไม่มี ยังไม่ได้ตรวจสอบประสิทธิภาพ
for range [N]int{} {
// Body...
}
ป.ล. วันแรกที่ GoLang กรุณาทำคำวิจารณ์ถ้ามันเป็นวิธีที่ผิด
non-constant array bound
ที่ฉัน
นอกจากนี้คุณยังสามารถดู 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 บางครั้งมันอาจจะมีประโยชน์