转到示例和成语[关闭]


91

没有很多Go语言代码可用于学习该语言,而且我敢肯定我不是唯一一个尝试过该语言的人。因此,如果您发现有关该语言的一些有趣信息,请在此处发布示例。

我也在寻找

  • Go中惯用的方式
  • C / C ++思维风格“移植”到Go,
  • 有关语法的常见陷阱,
  • 真的很有趣。

ARM支持,例如8位或16位。D语言仍然没有。

1
该库(golang.org/pkg)是学习如何使用go的绝佳资源。就个人而言,我发现学习数据结构的实现方式有助于学习该语言。
tkokasih 2013年

Answers:


35

推迟陈述

“ defer”语句调用一个函数,该函数的执行被推迟到周围函数返回的那一刻。

DeferStmt =“ defer”表达式。

表达式必须是函数或方法调用。每次执行“ 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)
}

17
看起来像旧的RAII(明确显示)。
康拉德·鲁道夫

4
+1,因为我阅读了很多有关Go的内容,但仍然看不到(直到您向我展示了)!
u0b34a0f6ae

聪明,虽然如果按照FIFO顺序(从上到下)执行语句,对我来说更有意义,但是也许就是我...
Mike Spross


4
@Mike:如果与“ try:.. finally:”块进行比较,则LIFO嵌套的方式相同。对于资源打开/关闭对等,这样的嵌套是唯一有意义的(第一个打开将最后关闭)。
u0b34a0f6ae

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 }

$$

!
<binary segment>

6
那比一个惯用的例子更像是一个隐藏的功能
hasen

22

我已经看到几个人抱怨“ for-loop”,类似“为什么i = 0; i < len; i++在这个时代必须说?”。

我不同意,我喜欢for结构。如果愿意,可以使用长版本,但是惯用的Go是

var a = []int{1, 2, 3}
for i, v := range a {
    fmt.Println(i, v)
}

for .. range构造在所有元素上循环并提供两个值-索引i和值v

range 也适用于地图和频道。

不过,如果您不喜欢for任何形式,可以在以下几行中定义eachmap等等:

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 :)


尽管range这只是很好,但是如果将其编译为与for-3循环相同的代码。
Thomas Ahle 2014年

19

去获得您的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"))
}

谢谢 Scott Wales对.Read()的帮助。

两个字符串和两个缓冲区看起来仍然很笨拙,所以如果有Go专家提出建议,请告诉我。


我不确定格式化应该出什么问题;我已经恢复了。

5
Go作者向gofmt您的代码推荐:-)
–aphink

我不能编译:$ ../go/src/cmd/6g/6g SO.go SO.go:34:未定义:json.StringToJson
ℝaphink

@Raphink:自从我这样做以来,语言已经发生了变化。

是的,您知道最接近StringToJson的是什么吗?它曾经在内部设置过一个构建器,现在必须为其提供一个预定义的本机结构吗?
macbirdie 2010年

19

这是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 (!)

5
注意,不需要分号。
mk12

18

您可以通过并行分配交换变量:

x, y = y, x

// or in an array
a[j], a[i] = a[i], a[j]

简单但有效。


18

这是来自“ 有效执行”页面的一个成语

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

此刻,交换机版本对我来说看起来更干净一些。


6
哇,完全被VB撕掉了。; Switch True-) (…)
Konrad Rudolph,2009年

@Konrad,击败我!:)我以前在VB6代码中使用过该惯用法,它肯定可以在某些情况下提高可读性。
Mike Spross

什么是“ <=”?与'<-'有关吗?
phaphink

@Raphink:小于或等于。
Paul Ruane

17

类型开关

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");
}


14

命名结果参数

可以给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;
}

1
我很好奇-还有其他语言有吗?
u0b34a0f6ae

1
Matlab有类似的东西。
丹·洛伦斯

pascal使用类似的语法返回一个值。
nes1983年

1
@ nes1983对于那些不知道的人,通常在Pascal中将返回值分配给函数名称。
2013年

FORTRAN几乎具有此功能。
2014年

14

詹姆斯·安提尔的答案

foo := <-ch     // This blocks.
foo, ok := <-ch // This returns immediately.

还有一个潜在的陷阱:接收和发送运算符之间的细微差别

a <- ch // sends ch to channel a
<-ch    // reads from channel ch

3
接收运营商本身就是现在阻塞操作,如围棋1.0.3。规范已修改:golang.org/ref/spec#Receive_operator。请在此处尝试阻止行为(死锁):play.golang.org/p/0yurtWW4Q3
Deleplace 2013年

13
/* 
 * 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))
}

4
我建议删除问题解决站点的名称以及ID号。也许改一下这个问题。为了不让问题绊倒人。或者尝试通过在网上搜索问题来作弊。
Mizipzor

1
记录:这是来自algorithmist.com/index.php/Coin_Change的算法。这是Google首次针对“硬币更改”产生的结果。
捷尔吉Andrasek

13

我喜欢您可以随意重新定义类型(包括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()


12

返回频道

这是一个非常重要的成语:如何将数据馈入通道并随后将其关闭。使用此工具,您可以创建简单的迭代器(因为范围将接受通道)或过滤器。

// 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;
}

+1。另外,您也可以通过频道传递频道。
捷尔吉Andrasek

5
但是请注意不要打破for x:= range chan {}循环,您将泄漏goroutine及其引用的所有内存。
杰夫·艾伦

3
@JeffAllen defer close(outch);作为goroutine的第一条语句怎么样?

1
无论函数返回哪个点,Defer都会在函数返回时将要执行的语句排队。但是,如果从不关闭通道输入,则此示例中的匿名函数将永远不会离开for循环。
杰夫·艾伦


11
for {
    v := <-ch
    if closed(ch) {
        break
    }
    fmt.Println(v)
}

由于range会自动检查是否存在封闭渠道,因此我们可以将其简化为:

for v := range ch {
    fmt.Println(v)
}

9

有一个可以在$ GOROOT / src中使用的make系统设置

用以下命令设置您的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中。


7

Go中另一个有趣的事情是godoc。您可以使用以下方法将其作为计算机上的Web服务器运行

godoc -http=:8080

其中8080​​是端口号,然后可以在golang.org上找到整个网站localhost:8080


这是常规程序还是守护程序?
捷尔吉Andrasek

这是一个常规程序。
杰里米

7

这是堆栈的实现。它说明了将方法添加到类型上。

我想将堆栈的一部分制成切片,并使用切片的属性,但是尽管没有使用, 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()
}

10
fmt.Printf(...); os.Exit();可以使用而不是使用panic(...)
notnoop

1
这给出了堆栈跟踪,这是我不想要的。

3
为什么受到限制?Go是一种托管的gc语言。您的堆栈可以任意深度。使用新的内建的append(),它将在需要时执行类似C的realloc的操作。
杰夫·艾伦

他们说:“ Go不需要泛型”。
cubuspl42

4

从Go调用C代码

通过使用c运行时,可以访问较低级别的go。

C函数的形式

void package·function(...)

(请注意,点分隔符是unicode字符),其中的参数可以是基本的go类型,slice,string等。返回值调用

FLUSH(&ret)

(您可以返回多个值)

例如,创建一个函数

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链接。


3
它真的使用“飞行点”吗?我不敢编辑,但这似乎有点出乎意料和激进。
放松

是的,您需要使用6c(或8c等)进行编译。我不认为gcc可以处理unicode标识符。
斯科特·威尔士,

1
我认为AltGr +句点类型相同·但是我不确定使用unicode。看到我阅读的源代码使我感到非常惊讶。为什么不使用::呢?
u0b34a0f6ae

字符是MIDDLE DOT U + 00B7。解析器可能已被捏造,以便将其视为一个字符,以便创建有效的c标识符,我相信它将排除::。
斯科特·威尔士,

4
“·”只是一个临时的hack,rob甚至对它仍然在那里感到惊讶,他说它将被不那么特质的东西所代替。
uriel


3

你看这个谈话了吗?它显示了很多您可以做的很酷的事情(结束语)


2
是的,我做到了。归结为“其中还有很多,让我们继续下一个主题。”
捷尔吉Andrasek

是的,显然可以用很少的时间说很多

3

基于其他答案的堆栈,但使用切片追加没有大小限制。

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

3
const ever = true

for ever {
    // infinite loop
}

25
for { /* infinite loop */ }足够。
u0b34a0f6ae

2
当然。这就是这里正在发生的事情。我只喜欢forever关键字。甚至Qt都有一个宏。
捷尔吉Andrasek

6
但是Go不需要宏或可爱的true别名即可执行此操作。
u0b34a0f6ae 2009年

@ kaizer.se:Jurily的要点是for ever(在声明变量之后),如果您愿意,可以在Go中完成。看起来像英语(取空白)。
Frank

8
您也可以在C语言中完成它。.::)#define ever (;;)
u0b34a0f6ae

2

test主目录中有很多小程序。例子:

  • peano.go 打印阶乘。
  • hilbert.go 有一些矩阵乘法。
  • iota.go 有一些怪异的iota事例。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.