Goの例とイディオム[終了]


91

言語を学ぶためのGoコードはそれほど多くはありません。私がそれを実験しているのは私だけではないと思います。ですから、言語について何か面白いものを見つけたら、ここに例を投稿してください。

私も探しています

  • Goで物事を行う慣用的な方法、
  • Goに「移植」されたC / C ++の考え方
  • 構文に関する一般的な落とし穴、
  • 本当に面白いものは何でも。

8ビットや16ビットなどのARMサポート。D言語はまだしていません。

1
ライブラリ(golang.org/pkg)は、goの使用方法を学ぶための優れたソースです。個人的には、データ構造がどのように実装されるかを学ぶことは、言語を学ぶのに役立ちます。
tkokasih 2013年

回答:


35

ステートメントを延期する

「defer」ステートメントは、周囲の関数が戻る瞬間まで実行が延期される関数を呼び出します。

DeferStmt = "defer" Expression。

式は、関数またはメソッドの呼び出しでなければなりません。「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のように見える(明示的)。
Konrad Rudolph、

4
Goについてたくさん読んだので+1しましたが、まだ表示されていません(あなたに見せられるまで)。
u0b34a0f6ae 2009年

賢いですが、ステートメントをFIFO順(上から下)で実行する場合は延期する方が理にかなっていますが、それはおそらく私だけです...
Mike Spross 2009年

涼しい。Dからscropガードを思い出させるdigitalmars.com/d/2.0/exception-safe.html
HASEN

4
@マイク:「try:.. finally:」のブロックと比較すると、LIFOは同じ方法でネストします。リソースのオープン/クローズのペアなどの場合、このようなネストは、意味のある唯一のものです(最初のオープンは最後にクローズします)。
u0b34a0f6ae 2009年

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

「なぜi = 0; i < len; i++この時代になぜ私たちは言わなければならないのか」という流れに沿って、for-loopについて不平を言う人が何人かいます。

私は同意しません、私は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が好きになり始めています:)


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

.Read()のヘルプを提供してくれたScott Walesに感謝します。

2つの文字列と2つのバッファがあるため、これはかなり不格好に見えます。そのため、Goの専門家がアドバイスを持っている場合は、私に知らせてください。


書式設定の何が問題だったのかわかりません。復元しました。

5
囲碁著者は、にお勧めしますgofmt:-)あなたのコード
ℝaphink

json.StringToJson:$ ../go/src/cmd/6g/6g SO.go SO.go:34:未定義私はそれをコンパイルすることはできません
ℝaphink

@Raphink:これを作成してから言語が変わった。

ええ、StringToJsonに最も近いものは何でしょうか?以前はビルダーを内部で設定していたが、今では事前定義されたネイティブ構造を独自に提供する必要があるのか​​。
macbirdie

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 2013

18

並列代入により変数を交換できます:

x, y = y, x

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

シンプルですが効果的です。


18

これは、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

現時点では、スイッチのバージョンは少しきれいに見えます。


6
おっと、VBから完全にリッピングされました。;Switch True
Konrad Rudolph

@Konrad、私を倒して!:)以前にVB6コードでそのイディオムを使用したことがあり、特定の状況で読みやすさを確実に向上させることができます。
Mike Spross、2009年

「<=」とは何ですか?「<-」に関連していますか?
ℝaphink

@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 2009年

1
Matlabにも同様の機能があります。
Dan Lorenc 2009年

pascalは、1つの値を返すために同様の構文を使用します。
nes1983

1
@ nes1983知らない人のために、Pascalでは、古典的に戻り値を関数名に割り当てます。
fuz

FORTRANはこれをほとんど持っています。
Hut8 2014年

14

ジェームズAntillの答え

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

また、潜在的な落とし穴:受信オペレーターと送信オペレーターの微妙な違い

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

3
Go 1.0.3以降、受信演算子自体ブロック操作になります。仕様が変更されました:golang.org/ref/spec#Receive_operator。ここでブロッキング動作(デッドロック)を試してください:play.golang.org/p/0yurtWW4Q3
Deleplace

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 2009年

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 {}ループから抜け出さないように注意してください。ゴルーチンとそれが参照するすべてのメモリがリークします。
ジェフアレン

3
@JeffAllenはゴルーチンdefer close(outch);の最初のステートメントとしてどうですか?

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システムがセットアップされています

でメイクファイルを設定する

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のもう1つの興味深い点は、それgodocです。あなたはそれを使ってあなたのコンピュータのウェブサーバーとしてそれを動かすことができます

godoc -http=:8080

ここで、8080はポート番号で、golang.orgのWebサイト全体はで利用できますlocalhost:8080


これは通常のプログラムですか、それともデーモンですか?
ジェルジAndrasek

通常のプログラムです。
Jeremy

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 2009年

1
スタックトレースが表示されますが、これは望ましくありません。

3
なぜそれが制限されているのですか?Goは、gcで管理された言語です。スタックは好きなだけ深くすることができます。新しいappend()ビルトインを使用します。これにより、必要に応じてCのreallocのようなことが行われます。
ジェフアレン

「Goはジェネリックを必要としない」と彼らは言った。
cubuspl42 2015年

4

GoからCコードを呼び出す

cランタイムを使用して、低レベルのgoにアクセスできます。

C関数は次の形式です

void package·function(...)

(ドットセパレーターはユニコード文字であることに注意してください)引数は基本的なgoタイプ、スライス、文字列などです。値の呼び出しを返すには

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を参照してください。

goでc ++コードをリンクする方法については、この回答もご覧ください。


3
本当に「フライングドット」を使っていますか?私はあえて編集しませんが、それは少し予想外で過激なようです。
アンワインド

はい、6c(または8cなど)でコンパイルする必要があります。gccがUnicode識別子を処理しないと思います。
スコットウェールズ

1
AltGr +ピリオドタイプは同じと思いますが、Unicodeではわかりません。私が読んだソースでそれを見て驚いたのは。::のようなものをなぜ使用しないのですか?
u0b34a0f6ae 2009年

キャラクターはミドルドットU + 00B7。パーサーは、有効なc識別子を作成するためにこれを文字と見なすように変更されている可能性があります。
スコットウェールズ

4
「・」は単なる一時的なハックであり、ロブはそれがまだそこにあることに驚きさえしました、彼はそれがより特異でない何かと取り替えられるであろうと言いました。
uriel 09年


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 2009年

2
もちろん。それがまさにここで起こっていることです。foreverキーワードが好きです。Qtにもそのためのマクロがあります。
ジェルジAndrasek

6
しかし、Goはこれを行うためにマクロやtrueのかわいいエイリアスを必要としません。
u0b34a0f6ae 2009年

@ kaizer.se:Jurilyのポイントは、for ever(変数を宣言した後)必要に応じてGoで実行できるかわいいことです。それは英語のように見えます(ブランクを法として)。
フランク

8
それはあなたがよくとしてCで行うことができますかわいい何か..です:-)#define ever (;;)
u0b34a0f6ae

2

testメインディレクトリにはたくさんの小さなプログラムがあります。例:

  • peano.go 階乗を出力します。
  • hilbert.go 行列の乗算があります。
  • iota.go 奇妙なイオタの例があります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.