没有很多Go代码可以从中学习语言,我确信我不是唯一一个尝试使用它的人.所以,如果您发现了有关该语言的有趣内容,请在此处发布示例.
我也在找
在Go中做事的惯用方法,
C/C++的思维方式"移植"到Go,
关于语法的常见缺陷,
有趣的,真的.
György Andra.. 35
推迟发言
"defer"语句调用一个函数,该函数的执行被推迟到周围函数返回的那一刻.
DeferStmt ="延迟"表达式.
表达式必须是函数或方法调用.每次执行"defer"语句时,都会评估函数调用的参数并重新保存,但不会调用该函数.延迟函数调用在周围函数返回之前立即以LIFO顺序执行,但在返回值(如果有)之后已经过评估.
lock(l); defer unlock(l); // unlocking happens before surrounding function returns // prints 3 2 1 0 before surrounding function returns for i := 0; i <= 3; i++ { defer fmt.Print(i); }
更新:
defer
现在也是panic
以异常方式处理的惯用方法:
package main import "fmt" func main() { f() fmt.Println("Returned normally from f.") } func f() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered in f", r) } }() fmt.Println("Calling g.") g(0) fmt.Println("Returned normally from g.") } func g(i int) { if i > 3 { fmt.Println("Panicking!") panic(fmt.Sprintf("%v", i)) } defer fmt.Println("Defer in g", i) fmt.Println("Printing in g", i) g(i+1) }
看起来好老RAII(明确表示). (17认同)
+1,因为我读了很多关于Go,但我仍然没有看到这个(直到你给我看)! (4认同)
@Mike:如果你与"try:.. finally:"的块比较,LIFO以同样的方式嵌套.对于资源打开/关闭对等,这样的嵌套是唯一有意义的事情(首先打开将关闭最后). (4认同)
到底有什么好处呢? (3认同)
György Andra.. 25
Go对象文件实际上包含一个明文标题:
jurily@jurily ~/workspace/go/euler31 $ 6g euler31.go jurily@jurily ~/workspace/go/euler31 $ cat euler31.6 amd64 exports automatically generated from euler31.go in package "main" import $$ // exports package main var main.coin [9]int func main.howmany (amount int, max int) (? int) func main.main () var main.initdone· uint8 func main.init () $$ // local types type main.dsigddd_1·1 struct { ? int } $$ !
这更像是隐藏的功能,而不是惯用的例子 (6认同)
棒极了! (2认同)
j-g-faustus.. 22
我看到有几个人抱怨for循环,"我们为什么要i = 0; i < len; i++
在这个时代说出来?".
我不同意,我喜欢for constru.如果你愿意,你可以使用长版本,但是惯用的Go是
var a = []int{1, 2, 3} for i, v := range a { fmt.Println(i, v) }
所述for .. range
构建体循环遍历所有元素,并提供两个值-索引i
和值v
.
range
也适用于地图和频道.
不过,如果你不喜欢for
任何形式,你可以定义each
,map
等在几行:
type IntArr []int // 'each' takes a function argument. // The function must accept two ints, the index and value, // and will be called on each element in turn. func (a IntArr) each(fn func(index, value int)) { for i, v := range a { fn(i, v) } } func main() { var a = IntArr([]int{2, 0, 0, 9}) // create int slice and cast to IntArr var fnPrint = func(i, v int) { fmt.Println(i, ":", v) } // create a function a.each(fnPrint) // call on each element }
版画
0 : 2 1 : 0 2 : 0 3 : 9
我开始喜欢Go了很多:)
推迟发言
"defer"语句调用一个函数,该函数的执行被推迟到周围函数返回的那一刻.
DeferStmt ="延迟"表达式.
表达式必须是函数或方法调用.每次执行"defer"语句时,都会评估函数调用的参数并重新保存,但不会调用该函数.延迟函数调用在周围函数返回之前立即以LIFO顺序执行,但在返回值(如果有)之后已经过评估.
lock(l); defer unlock(l); // unlocking happens before surrounding function returns // prints 3 2 1 0 before surrounding function returns for i := 0; i <= 3; i++ { defer fmt.Print(i); }
更新:
defer
现在也是panic
以异常方式处理的惯用方法:
package main import "fmt" func main() { f() fmt.Println("Returned normally from f.") } func f() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered in f", r) } }() fmt.Println("Calling g.") g(0) fmt.Println("Returned normally from g.") } func g(i int) { if i > 3 { fmt.Println("Panicking!") panic(fmt.Sprintf("%v", i)) } defer fmt.Println("Defer in g", i) fmt.Println("Printing in g", i) g(i+1) }
Go对象文件实际上包含一个明文标题:
jurily@jurily ~/workspace/go/euler31 $ 6g euler31.go jurily@jurily ~/workspace/go/euler31 $ cat euler31.6 amd64 exports automatically generated from euler31.go in package "main" import $$ // exports package main var main.coin [9]int func main.howmany (amount int, max int) (? int) func main.main () var main.initdone· uint8 func main.init () $$ // local types type main.dsigddd_1·1 struct { ? int } $$ !
我看到有几个人抱怨for循环,"我们为什么要i = 0; i < len; i++
在这个时代说出来?".
我不同意,我喜欢for constru.如果你愿意,你可以使用长版本,但是惯用的Go是
var a = []int{1, 2, 3} for i, v := range a { fmt.Println(i, v) }
所述for .. range
构建体循环遍历所有元素,并提供两个值-索引i
和值v
.
range
也适用于地图和频道.
不过,如果你不喜欢for
任何形式,你可以定义each
,map
等在几行:
type IntArr []int // 'each' takes a function argument. // The function must accept two ints, the index and value, // and will be called on each element in turn. func (a IntArr) each(fn func(index, value int)) { for i, v := range a { fn(i, v) } } func main() { var a = IntArr([]int{2, 0, 0, 9}) // create int slice and cast to IntArr var fnPrint = func(i, v int) { fmt.Println(i, ":", v) } // create a function a.each(fnPrint) // call on each element }
版画
0 : 2 1 : 0 2 : 0 3 : 9
我开始喜欢Go了很多:)
这是来自Kinopiko帖子的iota的一个很好的例子:
type ByteSize float64 const ( _ = iota; // ignore first value by assigning to blank identifier KB ByteSize = 1<<(10*iota) MB GB TB PB YB ) // This implicitly repeats to fill in all the values (!)
这是这个答案的翻译.
package main import ( "json" "fmt" "http" "os" "strings" ) func die(message string) { fmt.Printf("%s.\n", message); os.Exit(1); } func main() { kinopiko_flair := "/sf/ask/17360801/.json" response, _, err := http.Get(kinopiko_flair) if err != nil { die(fmt.Sprintf("Error getting %s", kinopiko_flair)) } var nr int const buf_size = 0x1000 buf := make([]byte, buf_size) nr, err = response.Body.Read(buf) if err != nil && error != os.EOF { die(fmt.Sprintf("Error reading response: %s", err.String())) } if nr >= buf_size { die ("Buffer overrun") } response.Body.Close() json_text := strings.Split(string(buf), "\000", 2) parsed, ok, errtok := json.StringToJson(json_text[0]) if ! ok { die(fmt.Sprintf("Error parsing JSON %s at %s", json_text, errtok)) } fmt.Printf("Your stackoverflow.com reputation is %s\n", parsed.Get ("reputation")) }
感谢Scott Wales对.Read()的帮助.
这看起来相当笨重,有两个字符串和两个缓冲区,所以如果Go专家有建议,请告诉我.
这是Effective Go页面中的一个成语
switch { case '0' <= c && c <= '9': return c - '0' case 'a' <= c && c <= 'f': return c - 'a' + 10 case 'A' <= c && c <= 'F': return c - 'A' + 10 } return 0
当没有给出表达式时,switch语句将为true.所以这相当于
if '0' <= c && c <= '9' { return c - '0' } else if 'a' <= c && c <= 'f' { return c - 'a' + 10 } else if 'A' <= c && c <= 'F' { return c - 'A' + 10 } return 0
目前,开关版本对我来说看起来更干净.
您可以通过并行分配交换变量:
x, y = y, x // or in an array a[j], a[i] = a[i], a[j]
简单但有效.
型号开关:
switch i := x.(type) { case nil: printString("x is nil"); case int: printInt(i); // i is an int case float: printFloat(i); // i is a float case func(int) float: printFunction(i); // i is a function case bool, string: printString("type is bool or string"); // i is an interface{} default: printString("don't know the type"); }
导入包时,您可以将名称重新定义为您想要的任何名称:
package main import f "fmt" func main() { f.Printf("Hello World\n") }
来自James Antill的回答:
foo := <-ch // This blocks. foo, ok := <-ch // This returns immediately.
此外,潜在的陷阱:接收和发送运营商之间的细微差别:
a <- ch // sends ch to channel a <-ch // reads from channel ch
命名结果参数
Go函数的返回或结果"参数"可以给出名称并用作常规变量,就像传入参数一样.命名时,它们在函数开始时被初始化为其类型的零值; 如果函数执行不带参数的return语句,则结果参数的当前值将用作返回值.
名称不是强制性的,但它们可以使代码更短更清晰:它们是文档.如果我们命名nextInt的结果,很明显哪个返回int是哪个.
func nextInt(b []byte, pos int) (value, nextPos int) {
由于命名结果已初始化并与简单的返回相关联,因此它们可以简化并澄清.这是一个使用它们的io.ReadFull版本:
func ReadFull(r Reader, buf []byte) (n int, err os.Error) { for len(buf) > 0 && err == nil { var nr int; nr, err = r.Read(buf); n += nr; buf = buf[nr:len(buf)]; } return; }
/* * How many different ways can £2 be made using any number of coins? * Now with 100% less semicolons! */ package main import "fmt" /* This line took me over 10 minutes to figure out. * "[...]" means "figure out the size yourself" * If you only specify "[]", it will try to create a slice, which is a reference to an existing array. * Also, ":=" doesn't work here. */ var coin = [...]int{0, 1, 2, 5, 10, 20, 50, 100, 200} func howmany(amount int, max int) int { if amount == 0 { return 1 } if amount < 0 { return 0 } if max <= 0 && amount >= 1 { return 0 } // recursion works as expected return howmany(amount, max-1) + howmany(amount-coin[max], max) } func main() { fmt.Println(howmany(200, len(coin)-1)) }
我喜欢你可以根据需要重新定义类型,包括像int这样的基元,并附加不同的方法.就像定义RomanNumeral类型一样:
package main import ( "fmt" "strings" ) var numText = "zero one two three four five six seven eight nine ten" var numRoman = "- I II III IV V VI VII IX X" var aText = strings.Split(numText, " ") var aRoman = strings.Split(numRoman, " ") type TextNumber int type RomanNumber int func (n TextNumber) String() string { return aText[n] } func (n RomanNumber) String() string { return aRoman[n] } func main() { var i = 5 fmt.Println("Number: ", i, TextNumber(i), RomanNumber(i)) }
打印出来的
Number: 5 five V
该RomanNumber()
调用本质上是一个强制转换,它将int类型重新定义为更具体的int类型.和Println()
调用String()
幕后.
这是一个非常重要的真实习惯:如何将数据提供给频道并在之后关闭它.有了这个,你可以制作简单的迭代器(因为范围将接受一个通道)或过滤器.
// return a channel that doubles the values in the input channel func DoublingIterator(input chan int) chan int { outch := make(chan int); // start a goroutine to feed the channel (asynchronously) go func() { for x := range input { outch <- 2*x; } // close the channel we created and control close(outch); }(); return outch; }
for { v := <-ch if closed(ch) { break } fmt.Println(v) }
由于range会自动检查一个封闭的通道,我们可以缩短到这个:
for v := range ch { fmt.Println(v) }
频道读取超时:
ticker := time.NewTicker(ns); select { case v := <- chan_target: do_something_with_v; case <- ticker.C: handle_timeout; }
从戴维斯刘被盗.
有一个make系统设置,你可以在$ GOROOT/src中使用
用你的makefile设置
TARG=foobar # Name of package to compile GOFILES=foo.go bar.go # Go sources CGOFILES=bang.cgo # Sources to run cgo on OFILES=a_c_file.$O # Sources compiled with $Oc # $O is the arch number (6 for x86_64) include $(GOROOT)/src/Make.$(GOARCH) include $(GOROOT)/src/Make.pkg
然后,您可以通过运行make test来使用自动化测试工具,或者使用make install将cgo中的包和共享对象添加到$ GOROOT.
这是堆栈的实现.它说明了在类型上添加方法.
我想把它的堆栈部分变成一个切片并使用切片的属性,但是虽然我没有使用它,但我type
看不到用于定义切片的语法type
.
package main import "fmt" import "os" const stack_max = 100 type Stack2 struct { stack [stack_max]string size int } func (s *Stack2) push(pushed_string string) { n := s.size if n >= stack_max-1 { fmt.Print("Oh noes\n") os.Exit(1) } s.size++ s.stack[n] = pushed_string } func (s *Stack2) pop() string { n := s.size if n == 0 { fmt.Print("Underflow\n") os.Exit(1) } top := s.stack[n-1] s.size-- return top } func (s *Stack2) print_all() { n := s.size fmt.Printf("Stack size is %d\n", n) for i := 0; i < n; i++ { fmt.Printf("%d:\t%s\n", i, s.stack[i]) } } func main() { stack := new(Stack2) stack.print_all() stack.push("boo") stack.print_all() popped := stack.pop() fmt.Printf("Stack top is %s\n", popped) stack.print_all() stack.push("moo") stack.push("zoo") stack.print_all() popped2 := stack.pop() fmt.Printf("Stack top is %s\n", popped2) stack.print_all() }
Go中另一个有趣的事情是godoc
.您可以使用在计算机上将其作为Web服务器运行
godoc -http=:8080
其中8080是端口号,golang.org上的整个网站随后可用localhost:8080
.