去例子和成语
学习语言的Go代码并不多,而且我确定我不是唯一一个用这个语言进行实验的人。 所以,如果你发现了一些关于该语言的有趣的东西,请在这里发表一个例子。
我也在找
- 在Go中做事的惯用方式,
- C / C ++思维“移植”到Go,
- 语法常见的陷阱,
- 真的很有意思
延迟陈述
“延迟”语句调用执行延迟到周围函数返回时的函数。
DeferStmt =“推迟”expression式。
expression式必须是函数或方法调用。 每次执行“延迟”语句时,都会重新评估和保存函数调用的参数,但不调用该函数。 延迟函数调用紧接在周围函数返回之前以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 } $$ ! <binary segment>
我看到有几个人抱怨这个循环,就像“为什么我们不得不在这个时代说i = 0; i < len; i++
在这个时代呢?
我不同意,我喜欢这个构造。 如果你愿意,你可以使用长版本,但是习惯性的Go是
var a = []int{1,2,3}; for i, v := range a { fmt.Println(i, v); }
for .. range
构造遍历所有元素并提供两个值 – 索引i
和值v
。
range
也适用于地图和频道。
不过,如果你不喜欢任何forms,你可以在几行中定义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
我开始喜欢去很多:)
去获得你的stackoverflow声誉
这是这个答案的翻译。
package main import ( "json" "fmt" "http" "os" "strings" ) func die(message string) { fmt.Printf("%s.\n", message); os.Exit(1); } func main() { kinopiko_flair := "https://stackoverflow.com/users/flair/181548.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")) }
感谢斯科特威尔士求助于.Read()。
这看起来相当笨重,两个string和两个缓冲区,所以如果任何专家有build议,让我知道。
这是Kinopiko的post里的一个很好的例子:
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 (!)
这里有一个来自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
当没有给出expression式时,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;
目前,开关版本看起来更清洁。
您可以通过并行赋值交换variables:
x, y = y, x // or in an array a[j], a[i] = a[i], a[j]
简单而有效。
types开关 :
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"); }
命名结果参数
Go函数的返回值或结果“参数”可以被赋予名称,并用作常规variables,就像传入的参数一样。 当命名时,它们在函数开始时被初始化为它们types的零值; 如果函数执行一个不带参数的返回语句,那么结果参数的当前值被用作返回值。
这些名字并不是强制性的,但是它们可以使代码更简洁明了:它们是文档。 如果我们命名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; }
从James Antill的回答 :
foo := <-ch; // This blocks. foo, ok := <-ch; // This returns immediately.
此外,还有一个潜在的缺陷:接收和发送操作员之间的细微差别 :
a <- ch; // sends ch to channel a <-ch; // reads from channel ch
/* * 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)) }
我喜欢你可以重新定义types,包括象int这样的基本types,只要你喜欢,并附上不同的方法。 就像定义一个RomanNumeraltypes一样:
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, " ", 0) var aRoman = strings.Split(numRoman, " ", 0) 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()
调用本质上是一个RomanNumber()
,它将inttypes重新定义为更具体的inttypes。 和Println()
在幕后调用String()
。
返回一个频道
这是一个非常重要的真实习惯用法,那就是如何将数据提供给频道,然后closures频道。 有了这个,你可以做简单的迭代器(因为范围将接受一个通道)或filter。
// 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) }
由于范围自动检查一个封闭的频道,我们可以缩短到这个:
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来使用自动化testing工具,或者使用make install将cgo中的包和共享对象添加到$ GOROOT中。
这是一个堆栈的实现。 它演示了向types添加方法。
我想把堆栈的一部分放到一个slice中,然后使用这个slice的属性,但是虽然没有这个type
,但是我看不到用type
定义一个slice的语法。
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
。
从Go调用c代码
可以通过使用c运行时访问较低级别的进程。
C函数的forms
void package·function(...)
(注意点分隔符是一个Unicode字符)参数可能是基本的types,切片,string等。要返回值调用
FLUSH(&ret)
(你可以返回多个值)
例如,创build一个函数
package foo bar( a int32, b string )(c float32 ){ c = 1.3 + float32(a - int32(len(b)) }
在C你使用
#include "runtime.h" void foo·bar(int32 a, String b, float32 c){ c = 1.3 + a - b.len; FLUSH(&c); }
请注意,您仍然应该将该函数声明为go文件,并且您必须自己处理内存。 我不确定是否可以使用这个调用外部库,使用cgo可能会更好。
查看运行时中使用的示例$ GOROOT / src / pkg / runtime。
另请参阅将c ++代码与go关联的答案 。
你有看这个演讲吗? 它显示了很多很酷的东西,你可以做(谈话结束)
const ever = true; for ever { //infinite loop }
这是一个使用sqlite3包的例子。
基于另一个答案,但使用切片追加的堆栈没有大小限制。
package main import "fmt" import "os" type Stack2 struct { // initial storage space for the stack stack [10]string cur []string } func (s *Stack2) push(pushed_string string) { s.cur = append(s.cur, pushed_string) } func (s *Stack2) pop() (popped string) { if len(s.cur) == 0 { fmt.Print("Underflow\n") os.Exit(1) } popped = s.cur[len(s.cur)-1] s.cur = s.cur[0 : len(s.cur)-1] return } func (s *Stack2) print_all() { fmt.Printf("Stack size is %d\n", len(s.cur)) for i, s := range s.cur { fmt.Printf("%d:\t%s\n", i, s) } } func NewStack() (stack *Stack2) { stack = new(Stack2) // init the slice to an empty slice of the underlying storage stack.cur = stack.stack[0:0] return } func main() { stack := NewStack() 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() }
主目录中有很多小程序正在test
中。 例子:
-
peano.go
打印阶乘。 -
hilbert.go
有一些matrix乘法。 -
iota.go
有奇怪的iota的事例。