A Tour of Go

Loading slides...
    ようこそ

    Hello, 世界

    Go言語ツアーへようこそ!

    このツアーは3つのセクションに分かれています。各セクションの最後に、 修了するための一連の課題があります。

    このツアーはインタラクティブです。 コンパイルとこのプログラムを リモートサーバ このコンピュータ 上で実行するために、 いますぐ、RUNボタンをクリック(またはShift+Enter)してみてください。 実行結果はコードの下部に表示されます。

    これらのサンプルプログラムは、Goの様々な側面を示しています。 このツアーのプログラムは、Goを自分で実験してみる出発点となるでしょう。

    プログラムを編集して再度実行してみてください!

    次へ進む準備ができたら、NEXTボタンをクリックするか、Page Downキーを入力してください。

    注意!: 本サイトは、A Tour of Go を日本語訳したものです。

    package main import "fmt" func main() { fmt.Println("Hello, 世界") }

    Go offline

    このツアーは、あなたがインターネットへ接続せずに、スタンドアロン(stand-alone) のプログラムとして実行することもできるようになっています。

    スタンドアロンでのツアーは、自分のマシンでサンプルコードをビルドして実行するので、より高速です。 また、オンラインのバージョンでは、ここでは実行できない追加の練習問題を含んでいます。

    ツアーをローカルで実行するには、まず、 Goをインストール (このツアーは、Go version 1 (Go 1)を使用しています) し、そして、 gotour: をインストールするために go get コマンドを使います:

        go get code.google.com/p/go-tour/gotour

    そして、インストールした gotour を実行します。

    インストールしないのであれば、 "NEXT" ボタンか PageDown を押して次に進みましょう。

    ("INDEX" ボタンを押して、いつでもこのインストラクションへ戻ることができます。)

    Introduction

    Packages

    すべてのGoプログラムは、パッケージで構成されています。

    プログラムは mainパッケージ内で実行を開始します。

    このプログラムは、パッケージの "fmt""math" をインポートして使用しています。

    ルールとして、パッケージの名前はインポートパスの最後の単語と同じです。

    package main import ( "fmt" "math" ) func main() { fmt.Println("Happy", math.Pi, "Day") }

    Imports

    このコードでは、括弧でインポートをグループ化し、要素化した(factored)インポートステートメントとしています。 もしろん、複数のimportステートメントを用いて以下のように書くこともできます:

    	    import "fmt"
    	    import "math"
    	  
    ですが、バラバラになるのを避けるため、パッケージをまとめた形で使うのが一般的です。
    package main import ( "fmt" "math" ) func main() { fmt.Printf("Now you have %g problems.", math.Nextafter(2, 3)) }

    Exported names

    パッケージをインポートした後、そのパッケージが外部に公開している名前を参照することができます。

    Goでは、変数や関数の最初の文字が大文字で始まる場合は、その名前は外部から参照できる名前になります。

    Pi (π)は外部へ公開された名前ですが、 pi は公開されていません。

    コードを実行してみてください。そして、math.pimath.Pi に書き換えてもう一度実行してみてください。

    package main import ( "fmt" "math" ) func main() { fmt.Println(math.pi) }

    Functions

    関数は0個以上の引数を取ることができます。

    この例では、add関数は int型の2つのパラメータを取ります。

    型が変数名のにくることに注意してください。

    (なぜ型をこのように宣言するか、より詳細な情報については、記事「Go's declaration syntax」を参照してください。)

    package main import "fmt" func add(x int, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) }

    Functions

    2つ以上の関数の引数が同じ型である場合は、 最後の型を残して省略することができます。

    この例では、

    x int, y int

    x, y int
    へ省略できます。
    package main import "fmt" func add(x, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) }

    Functions

    関数は複数の戻り値を返すことができます。

    このswap関数は2つのstringを返します。

    package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("hello", "world") fmt.Println(a, b) }

    Functions

    関数はパラメータを取ることができます。Goでの関数は、ひとつだけでなく、 複数の戻り値パラメータ(result parameters)を返すことができます。 それらには、名前を付け、そして、変数のように扱うことができます。

    もし戻り値パラメータが名前付けられているならば、returnステートメントに 戻り値の変数名を書く必要はありません。

    package main import "fmt" func split(sum int) (x, y int) { x = sum * 4/9 y = sum - x return } func main() { fmt.Println(split(17)) }

    Variables

    varステートメントは、値のリストを宣言します。 関数の引数リストのように型は最後に書きます。

    package main import "fmt" var x, y, z int var c, python, java bool func main() { fmt.Println(x, y, z, c, python, java) }

    Variables

    var宣言は、変数1つ毎に初期化子(initializer)を与えることができます。

    もし初期化子が指定されている場合、型を省略することができます。 その変数は初期化子の型を得ることになります。

    package main import "fmt" var x, y, z int = 1, 2, 3 var c, python, java = true, false, "no!" func main() { fmt.Println(x, y, z, c, python, java) }

    Variables

    関数内では、var宣言の代わりに、暗黙的な型宣言ができる:=の代入文を 使うことができます。

    (関数外では、すべての宣言は、キーワードから始まり、:=での宣言は利用できません。)

    package main import "fmt" func main() { var x, y, z int = 1, 2, 3 c, python, java := true, false, "no!" fmt.Println(x, y, z, c, python, java) }

    Constants

    定数(Constant)は、変数のように宣言されますが、 constキーワードを使って宣言します。

    定数は、character、string、boolean、数値(numeric values)のみで使えます。

    package main import "fmt" const Pi = 3.14 func main() { const World = "世界" fmt.Println("Hello", World) fmt.Println("Happy", Pi, "Day") const Truth = true fmt.Println("Go rules?", Truth) }

    Numeric Constants

    数値定数は高精度(high-precision)(values)です。

    型のない定数は、コンテキストによって必要な型を取ることになります。

    needInt(Big) を出力してみてください。

    package main import "fmt" const ( Big = 1<<100 Small = Big>>99 ) func needInt(x int) int { return x*10 + 1 } func needFloat(x float64) float64 { return x*0.1 } func main() { fmt.Println(needInt(Small)) fmt.Println(needFloat(Small)) fmt.Println(needFloat(Big)) }

    For

    Goは、forループだけを繰り返し文として使います。

    基本的にはC言語やJavaと同じですが、括弧( )は必要なく(付けてはいけません)、 中括弧{ }は必須です。

    package main import "fmt" func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) }

    For

    C言語やJavaのように、条件の前と後を空白のままにすることができます。

    package main import "fmt" func main() { sum := 1 for ; sum < 1000; { sum += sum } fmt.Println(sum) }

    For

    セミコロンを抜くこともできます。つまり、 C言語でのwhileは、Goではforと書きます。

    package main import "fmt" func main() { sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum) }

    For

    もし、ループ条件を省略すると、無限ループになります。

    package main func main() { for ; ; { } }

    For

    さらにセミコロンを省略できるので、無限ループをコンパクトに表現できます。

    package main func main() { for { } }

    If

    ifステートメントはC言語やJavaと同じようですが、 括弧( )は必要なく(付けてはいけません)、 中括弧{ }は必須です。

    (おなじみですね!)

    package main import ( "fmt" "math" ) func sqrt(x float64) string { if x < 0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) } func main() { fmt.Println(sqrt(2), sqrt(-4)) }

    If

    forのように、ifステートメントは、 短い文を条件の前に書くことができます。

    ここで宣言された変数は、ifのスコープだけで有効です。

    (最後のreturn文で、vを使ってみてください。)

    package main import ( "fmt" "math" ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) }

    If

    ifの短い文で宣言された変数は、 elseのブロック内でも使うことができます。

    package main import ( "fmt" "math" ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } else { fmt.Printf("%g >= %g\n", v, lim) } // can't use v here, though return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) }

    Basic types

    Go言語の基本的な型は以下です:

    bool
    
    string
    
    int  int8  int16  int32  int64
    uint uint8 uint16 uint32 uint64 uintptr
    
    byte // alias for uint8
    
    rune // alias for int32
         // represents a Unicode code point
    
    float32 float64
    
    complex64 complex128
    	
    package main import ( "math/cmplx" "fmt" ) var ( ToBe bool = false MaxInt uint64 = 1<<64 - 1 z complex128 = cmplx.Sqrt(-5+12i) ) func main() { const f = "%T(%v)\n" fmt.Printf(f, ToBe, ToBe) fmt.Printf(f, MaxInt, MaxInt) fmt.Printf(f, z, z) }

    Structs

    struct(構造体)は、フィールド(field)の集まりです。

    (そして、type宣言はあなたが期待しているものです。)

    package main import "fmt" type Vertex struct { X int Y int } func main() { fmt.Println(Vertex{1, 2}) }

    Struct Fields

    構造体のフィールドは、ドット(.)を用いてアクセスします。

    package main import "fmt" type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) }

    Pointers

    Go言語にはポインタがありますが、ポインタ演算はありません。

    構造体のフィールドは、構造体のポインタを介してアクセスできます。 このポインタを通じた間接的なアクセスは明確です。

    package main import "fmt" type Vertex struct { X int Y int } func main() { p := Vertex{1, 2} q := &p q.X = 1e9 fmt.Println(p) }

    Struct Literals

    structリテラルは、structのフィールドの値を記述することで、 新しく割り当てられたstructを表しています。

    Name:構文を使って、フィールドの一部だけを記述することができます。 (なお、名前のフィールドの順序は無関係です。)

    特別な接頭辞の&は、structリテラルへのポインタを作成します。

    package main import "fmt" type Vertex struct { X, Y int } var ( p = Vertex{1, 2} // has type Vertex q = &Vertex{1, 2} // has type *Vertex r = Vertex{X: 1} // Y:0 is implicit s = Vertex{} // X:0 and Y:0 ) func main() { fmt.Println(p, q, r, s) }

    The new function

    new(T)という表現は、ゼロ初期化した(zeroed)Tの値を メモリに割り当て、そのポインタを返します。

    var t *T = new(T)

    または、

    t := new(T)
    と記述できます。
    package main import "fmt" type Vertex struct { X, Y int } func main() { v := new(Vertex) fmt.Println(v) v.X, v.Y = 11, 9 fmt.Println(v) }

    Maps

    mapはキーと値とを関連付けます。

    mapは、使用する前にmake(newではありません)で作成する必要があります。 nilのmapは空であり、割り当てることはできません。

    package main import "fmt" type Vertex struct { Lat, Long float64 } var m map[string]Vertex func main() { m = make(map[string]Vertex) m["Bell Labs"] = Vertex{ 40.68433, 74.39967, } fmt.Println(m["Bell Labs"]) }

    Maps

    mapリテラルは、structリテラルに似ていますが、キーが必要です。

    package main import "fmt" type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ "Bell Labs": Vertex{ 40.68433, -74.39967, }, "Google": Vertex{ 37.42202, -122.08408, }, } func main() { fmt.Println(m) }

    Maps

    もし、トップレベルの型が型名である場合は、リテラルの要素からその型名を省略することができます。

    package main import "fmt" type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ "Bell Labs": {40.68433, -74.39967}, "Google": {37.42202, -122.08408}, } func main() { fmt.Println(m) }

    Slices

    sliceは、値の配列を参照し、長さ(length)も含まれています。

    []Tは、T型の要素をもつsliceです。

    package main import "fmt" func main() { p := []int{2, 3, 5, 7, 11, 13} fmt.Println("p ==", p) for i := 0; i < len(p); i++ { fmt.Printf("p[%d] == %d\n", i, p[i]) } }

    Slices

    sliceは、同じ配列を参照する新しいsliceを作成することで、再sliceすることができます。

    s[lo:hi]

    という表現では、loからhi-1までの要素のsliceを導いています。 そのため、

    s[lo:lo]

    は、空で、

    s[lo:lo+1]

    は、ひとつの要素をもつことになります。

    package main import "fmt" func main() { p := []int{2, 3, 5, 7, 11, 13} fmt.Println("p ==", p) fmt.Println("p[1:4] ==", p[1:4]) // missing low index implies 0 fmt.Println("p[:3] ==", p[:3]) // missing high index implies len(s) fmt.Println("p[4:] ==", p[4:]) }

    Slices

    sliceは、make関数で作成されます。 これは、ゼロ化した配列をメモリに割り当て、その配列を参照してsliceを返す働きをします。

    a := make([]int, 5)  // len(a)=5
    	
    sliceは、長さと容量(capacity)を持っています。 sliceの容量は、sliceが基礎となる配列で拡大できる最大の長さです。

    容量を指定するためには、makeの3番目の引数に渡します:

    b := make([]int, 0, 5)
    // len(b)=0, cap(b)=5
    	
    sliceは再スライス(re-slicing)によって拡大させることができます(最大容量まで):

    b = b[:cap(b)] // len(b)=5, cap(b)=5
    b = b[1:]      // len(b)=4, cap(b)=4
    	
    package main import "fmt" func main() { a := make([]int, 5) printSlice("a", a) b := make([]int, 0, 5) printSlice("b", b) c := b[:2] printSlice("c", c) d := c[2:5] printSlice("d", d) } func printSlice(s string, x []int) { fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x) }

    Slices

    sliceのゼロの値はnilです。

    nil sliceは、0の長さと容量を持っています。

    (sliceについて詳しく学ぶためには、 "Slices: usage and internals" を読んでみてください)

    package main import "fmt" func main() { var z []int fmt.Println(z, len(z), cap(z)) if z == nil { fmt.Println("nil!") } }

    Functions

    関数も値で扱えます。

    package main import ( "fmt" "math" ) func main() { hypot := func(x, y float64) float64 { return math.Sqrt(x*x + y*y) } fmt.Println(hypot(3, 4)) }

    Functions

    また、関数は完全なクロージャ(closure)です。

    adder関数はクロージャを返しています。 各クロージャは、それ自身のsum変数へ紐付けされます。

    package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i), ) } }

    Range

    forループのrangeフォームは、sliceやmapをひとつずつ反復処理します。

    package main import "fmt" var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} func main() { for i, v := range pow { fmt.Printf("2**%d = %d\n", i, v) } }

    Range

    キーや値を "_"(アンダーバー) へ代入することでスキップすることができます。

    もしインデックスだけが必要なら、例のコードで “, value” の部分を削除します。

    package main import "fmt" func main() { pow := make([]int, 10) for i := range pow { pow[i] = 1<<uint(i) } for _, value := range pow { fmt.Printf("%d\n", value) } }

    Switch

    みなさんは、switchがどのようなものかを知っていることでしょう。

    Goでのswitchは、caseの最後で自動的にbreakします。 もし、フォールスルーしたい場合は、fallthrough文をcaseの最後に記述します。

    package main import ( "fmt" "runtime" ) func main() { fmt.Print("Go runs on ") switch os := runtime.GOOS; os { case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: // freebsd, openbsd, // plan9, windows... fmt.Printf("%s.", os) } }

    Switch

    switch caseは、上から下へcaseを評価します。 caseの条件が一致すれば、そこで停止します。

    (例えば、

    switch i {
    case 0:
    case f():
    }

    というコードで、もし、caseの条件がi==0であれば、 fの関数を呼び出しません)

    package main import ( "fmt" "time" ) func main() { fmt.Println("When's Saturday?") today := time.Now().Weekday() switch time.Saturday { case today+0: fmt.Println("Today.") case today+1: fmt.Println("Tomorrow.") case today+2: fmt.Println("In two days.") default: fmt.Println("Too far away.") } }

    Switch

    条件のないswitchは、switch trueと書いたことと同じです。

    このswitchの構造は、長い"if-then-else"のつながりの記述を綺麗にできます。

    package main import ( "fmt" "time" ) func main() { t := time.Now() switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") } }

    Exercise: Loops and Functions

    関数とループを使った簡単な練習として、 ニュートン法を使った平方根の計算を実装してみましょう。

    この問題では、ニュートン法は、 開始点zを選び、以下の式を繰り返すことによって、 Sqrt(x)を近似します。

    最初は、その計算式を10回だけ繰り返し、 xを(1, 2, 3, ...)と様々な値に対する結果がどれだけ正解値に近いかを確認してみてください。

    次に、ループを回すときの直前に求めたzの値がこれ以上変化しなくなったとき (または、差がとても小さくなったとき) に停止するようにループを変更してみてください。 この変更により、ループ回数が多くなったか、少なくなったのか見てみてください。 math.Sqrtと比べてどれくらい近似できましたか?

    ヒント:浮動小数点を宣言し、値を初期化するには、型のキャストか、浮動小数点を使ってください:

    	z := float64(1)
    	z := 1.0
    	
    package main import ( "fmt" ) func Sqrt(x float64) float64 { } func main() { fmt.Println(Sqrt(2)) }

    Exercise: Maps

    WordCount関数を実装してみましょう。 文字列sの“word”の数のmapを戻す必要があります。 wc.Test関数は、引数に渡した関数に対しテストスイートを実行し、 成功か失敗かを結果に表示します。

    strings.Fields で、何かヒントを得ることができるはずです。

    package main import ( "tourcode.google.com/p/go-tour/wc" ) func WordCount(s string) map[string]int { return map[string]int{"x": 1} } func main() { wc.Test(WordCount) }

    Exercise: Slices

    Pic関数を実装してみましょう。 このプログラムを実行すると、生成した画像が下に表示されるはずです。 この関数は、長さdyのsliceに、 各要素が8bitのunsigned int型の 長さdxのsliceを割り当てたものを を返す必要があります。 画像は、整数値をグレースケール(実際はブルースケール)として解釈したものです。

    生成する画像は、あなたが選択してください。 面白い関数として、x^y(x+y)/2、 and x*y があります。

    ヒント:([][]uint8に、各[]uint8を割り当てるためにループを使用する必要があります)

    ヒント:(uint8(intValue)を型の変換のために使います)

    補足:fmt.Printlnなどを用いて標準出力すると画像が表示されなくなってしまうため、使わないでください。

    package main import "tourcode.google.com/p/go-tour/pic" func Pic(dx, dy int) [][]uint8 { } func main() { pic.Show(Pic) }

    Exercise: Fibonacci closure

    関数のもつ、いくつかの面白いものを見てみましょう。

    fibonacci関数を実装しましょう。 この関数は、連続するフィボナッチ(fibonacci)数を返す関数(クロージャ)を返します。

    package main import "fmt" // fibonacci is a function that returns // a function that returns an int. func fibonacci() func() int { } func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } }

    Advanced Exercise: Complex cube roots

    complex64complex128 の型による、 Go言語の複素数(complex)について見てみましょう。 立方根(cube root)の場合、ニュートン法は、以下の式を繰り返すことになります:

    "2"の立方根を探し、アルゴリズムが正しく動作するか確認してください。 math/cmplxパッケージに Pow 関数がありますので、結果を確認してみましょう。

    package main import "fmt" func Cbrt(x complex128) complex128 { } func main() { fmt.Println(Cbrt(2)) }
    Methods and Interfaces

    Methods and Interfaces

    Methods

    Goは、クラス(class)を備えていません。 しかし、struct型にメソッド(method)を定義することができます。

    メソッドレシーバ(method receiver) は、funcキーワードとメソッド名の間で、 それ自身の引数リストで表現します。 (訳注:この例では、(v *Vertex)のことを言っている)

    package main import ( "fmt" "math" ) type Vertex struct { X, Y float64 } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := &Vertex{3, 4} fmt.Println(v.Abs()) }

    Methods

    実際には、structだけではなく、パッケージ内で任意の型にもメソッドを定義できます。

    しかし、他のパッケージからの型や基本型にはメソッドを定義できません。

    package main import ( "fmt" "math" ) type MyFloat float64 func (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f) } func main() { f := MyFloat(-math.Sqrt2) fmt.Println(f.Abs()) }

    Methods with pointer receivers

    メソッドは、名前付きの型(named type)や、名前付きの型へのポインタに関連付けられます。

    ここまでで、2つのAbs関数がありました。 ひとつは、*Vertexのポインタ型(pointer type)で、 もうひとつは、MyFloatの値型(value type)です。

    ポインタのレシーバ(pointer receiver)を使う2つの理由があります。 1つ目は、各メソッド呼び出しで、値をコピーするのを避けるためです。 (最も効果的なのは、値型が大きな構造体の場合です) 2つ目は、そのメソッドは、そのレシーバが指している値を変更できるようになります。

    AbsScaleメソッドの定義を、 *Vertexの代わりに、レシーバとしてVertex を使って書き換えてみましょう。

    Scaleメソッドは、 vVertexの場合は、 何の影響もありません。 Scalevを変化させます。 vが値(ポインタではない)型の場合は、メソッドは Vertexのコピーを参照し、元の値を変化させることはできません。

    Absは、どちらかの方法でも動作します。 vを読み出すだけです。 元の値を(ポインタを通じて)読み出し続けるか、値のコピーをするかはたいした問題ではありません。

    package main import ( "fmt" "math" ) type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := &Vertex{3, 4} v.Scale(5) fmt.Println(v, v.Abs()) }

    Interfaces

    インターフェース型(interface type)は、メソッド群で定義されます。

    インターフェース型の値は、それらのメソッドを実装する任意の値をもつことができます。

    package main import ( "fmt" "math" ) type Abser interface { Abs() float64 } func main() { var a Abser f := MyFloat(-math.Sqrt2) v := Vertex{3, 4} a = f // a MyFloat implements Abser a = &v // a *Vertex implements Abser a = v // a Vertex, does NOT // implement Abser fmt.Println(a.Abs()) } type MyFloat float64 func (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f) } type Vertex struct { X, Y float64 } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) }

    Interfaces

    ある型に、メソッドを実装することで、インターフェースを実装します。

    (明示的な定義の宣言はありません)

    この暗黙的なインターフェースは、 インターフェースを定義するパッケージと、 実装されているパッケージとを分離することができます。 (他に依存することはありません)

    これは、正確なインターフェースの定義を推奨しています。 なぜなら、あなたが すべての実装を見つける必要がないということと、 新しいインターフェースの名前でそれを関連付ける必要がないためです。

    パッケージ ioは、 ReaderWriterを定義しています。 ですので、あなたは何もする必要はありません。

    package main import ( "fmt" "os" ) type Reader interface { Read(b []byte) (n int, err error) } type Writer interface { Write(b []byte) (n int, err error) } type ReadWriter interface { Reader Writer } func main() { var w Writer // os.Stdout implements Writer w = os.Stdout fmt.Fprintf(w, "hello, writer\n") }

    Errors

    エラーは、エラー文字列としてエラーそのものを説明できるものです。 このしくみは、 組込みのインターフェース型errorに 文字列を返すひとつのメソッドError を事前に定義したことで表現されます:

    
    type error interface {
    	Error() string
    }
    	

    fmtパッケージのさまざまな表示ルーチンが errorを自動的に表示するように求められたときに そのメソッドを呼び出すことを識別します。

    package main import ( "fmt" "time" ) type MyError struct { When time.Time What string } func (e *MyError) Error() string { return fmt.Sprintf("at %v, %s", e.When, e.What) } func run() error { return &MyError{ time.Now(), "it didn't work", } } func main() { if err := run(); err != nil { fmt.Println(err) } }

    Web servers

    パッケージ http は、http.Handlerを実装する何かの値を用いることで、 HTTPリクエストの処理機能を提供します:

    package http
    
    type Handler interface {
    	ServeHTTP(w ResponseWriter,
    	          r *Request)
    }
    	

    この例では、 型Helloは、http.Handlerを実装します。

    "Hello!"を表示するには、http://localhost:4000/へアクセスしてください。 注意: この例は、ウェブベースのツアーでは実行できません。 このWebサーバを自分で動かしてみるには、 Goのインストール が必要となります。 (訳注:サンプルコードをコピーし、ローカルでコンパイルして動かしてみてください。)

    package main import ( "fmt" "net/http" ) type Hello struct{} func (h Hello) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello!") } func main() { var h Hello http.ListenAndServe("localhost:4000",h) }

    Images

    パッケージ imageは、 以下のImageインターフェースを定義しています:

    package image
    
    type Image interface {
    	ColorModel() color.Model
    	Bounds() Rectangle
    	At(x, y int) color.Color
    }

    (詳細は、 このドキュメント を参照してください。)

    また、color.Colorcolor.Modelは共にインターフェースです。 しかし、定義済みの color.RGBAcolor.RGBAModel を使うことによって、そのインターフェースを無視します。

    package main import ( "fmt" "image" ) func main() { m := image.NewRGBA(image.Rect(0, 0, 100, 100)) fmt.Println(m.Bounds()) fmt.Println(m.At(0, 0).RGBA()) }

    Exercise: Errors

    あなたのSqrt関数を以前の演習からコピーし、 errorの値を返すように修正してみてください。

    Sqrtは、 複素数をサポートしていないので、負の値が与えられたとき、 nil以外のエラー値を返す必要があります。

    新しいタイプの

    type ErrNegativeSqrt float64
    を作成してみてください。

    そして、error ErrNegativeSqrt(-2).String.Error()で、 "cannot Sqrt negative number: -2"を返すような

    func (e ErrNegativeSqrt) Error() string

    メソッドを作ります。

    注意: Errorメソッド中で、fmt.Print(e)を呼び出すことは、 無限ループにプログラムが陥ることでしょう。 最初に、fmt.Print(float64(e))としてeを変換することより、 これを避けることができます。 なぜでしょうか?

    負の値が与えられたとき、 ErrNegativeSqrtの値を返すように Sqrt関数を修正してみてください。

    package main import ( "fmt" ) func Sqrt(f float64) (float64, error) { return 0, nil } func main() { fmt.Println(Sqrt(2)) fmt.Println(Sqrt(-2)) }

    Exercise: HTTP Handlers

    以下の型を実装し、それらにServeHTTPメソッドを定義してみてください。 自分のWebサーバで、特定のパスを処理(ハンドル)できるように登録してみてください。

    type String string
    	
    type Struct struct {
    	Greeting string
    	Punct    string
    	Who      string
    }

    例えば、以下のような ハンドラを用いて登録できるようにする必要があります。

    http.Handle("/string", String("I'm a frayed knot."))
    http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"})
    package main import ( "net/http" ) func main() { // your http.Handle calls here http.ListenAndServe("localhost:4000", nil) }

    Exercise: Images

    前に書いた、画像ジェネレーターを覚えてますか? 他のものを書いてみましょう。 でも今回は、データのスライスの代わりに image.Image の実装を返すようにしてみます。

    あなたのImage型を定義して、 必要なメソッドを実装し、 pic.ShowImageを呼び出してみてください。

    Boundsは、 image.Rect(0, 0, w, h)のように image.Rectangleを返す必要があります。

    ColorModelは、 color.RGBAModelを返す必要があります。

    Atは、ひとつのcolorを返す必要があります。 最後の画像ジェネレーターの 値vは、 color.RGBA{v, v, 255, 255}のものと 一致します。

    package main import ( "image" "tourcode.google.com/p/go-tour/pic" ) type Image struct{} func main() { m := Image{} pic.ShowImage(m) }

    Exercise: Rot13 Reader

    ある共通のパターンは、io.Readerです。 これは、何らかの方法でストリームを変更する別のio.Readerをラップしています。

    例えば、 gzip.NewReader は、io.Reader(gzipされたデータのストリーム)を引数で受け取り、 *gzip.Readerを返します。 その*gzip.Readerは、io.Reader(展開されたデータのストリーム) を実装しています。

    io.Readerを実装し、 io.Readerから読み出すように rot13Readerを実装してみてください。 なお、rot13Readerは、 ROT13 換字式暗号(substitution cipher)を すべてのアルファベットの文字 に適用してください。

    rot13Reader型は、みなさんに提供します。 このrot13Readerは、 それのReadメソッドを実装することで io.Readerを作成してみてください。

    package main import ( "io" "os" "strings" ) type rot13Reader struct { r io.Reader } func main() { s := strings.NewReader( "Lbh penpxrq gur pbqr!") r := rot13Reader{s} io.Copy(os.Stdout, &r) }
    Concurrency

    Concurrency

    Goroutines

    goroutine(ゴルーチン)は、Go実行時に管理される軽量スレッドです。

    go f(x, y, z)

    で、fは新しいgoroutineで実行されます。

    f(x, y, z)

    fxyzの 評価は、現在のgoroutineで起き、 fの 実行は、新しいgoroutineで起こります。

    goroutineは、同じアドレス空間で実行されるため、 共有メモリへのアクセスは、同期する必要があります。 他のプリミティブ型(primitive)にあるように Goではあまり多く必要としませんが、 syncパッケージは 便利なプリミティブ型を提供します。 (次のスライドをみてください。)

    package main import ( "fmt" "runtimetime" ) func say(s string) { for i := 0; i < 5; i++ { runtime.Gosched()time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go say("world") say("hello") }

    Channels

    チャネル(Channel)は、 チャネルオペレータの<-を用いて、 値の送受信をできる直通ルートの型です。

    ch <- v    // Send v to channel ch.
    v := <-ch  // Receive from ch, and
               // assign value to v.
    

    (データは、矢印の方向に流れます。)

    マップとスライスのように、チャネルは、 使う前に以下のように生成する必要があります:

    ch := make(chan int)
    

    デフォルトでは、 片一方が準備できるまで、送受信はブロックされます。 これは、 明確なロックや、条件変数がなくても、goroutineの同期を許します。

    package main import "fmt" func sum(a []int, c chan int) { sum := 0 for _, v := range a { sum += v } c <- sum // send sum to c } func main() { a := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) x, y := <-c, <-c // receive from c fmt.Println(x, y, x + y) }

    Buffered Channels

    チャネルは、bufferedできます。 buffered channel の初期化で makeの2つ目の引数にバッファの長さを与えます:

    ch := make(chan int, 100)
    

    バッファがいっぱいになる時だけ、 buffered channel への送信をブロックします。 バッファが空の時は、受信をブロックします。

    バッファをいっぱいにするようにサンプルコードを変更し、 何が起きるのかを見てみてください。

    package main import "fmt" func main() { c := make(chan int, 2) c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) }

    Range and Close

    送り手は、 これ以上の送信する値がないことを示すため、 チャネルを close できます。 受け手は、 受信の式に 2つ目のパラメータを与えることで、 そのチャネルがcloseしてしまっているかどうかをテストできます。

    v, ok := <-ch

    もし、これ以上の受信する値がなく、チャネルが閉じているなら、 okは、falseになります。

    ループのfor i := range cは、 チャネルが閉じられるまで、繰り返しチャネルから値を受信します。

    注意: 受け手ではなく、送り手だけ、チャネルを閉じる必要があります。 閉じたチャネルへの送信は、パニック(panic)になるでしょう。

    もう一つ注意: チャネルは、ファイルと同じものではありません。 通常、それらを閉じる必要はありません。 閉じるのは、 これ以上来る値がないことを 受け手が知らせなければならないとき だけ必要です。 例えば、rangeループを終了することなどです。

    package main import ( "fmt" ) func fibonacci(n int, c chan int) { x, y := 1, 1 for i := 0; i < n; i++ { c <- x x, y = y, x + y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } }

    Select

    selectステートメントは、 goroutineを複数の通信操作で待たせます。

    selectは、 それのcaseの条件で、いずれかを実行できるようになるまでブロックし、 そして、条件が一致したcaseを実行します。 もし、複数が一致した場合、caseはランダムに選ばれます。

    package main import "fmt" func fibonacci(c, quit chan int) { x, y := 1, 1 for { select { case c <- x: x, y = y, x + y case <-quit: fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) }

    Default Selection

    selectの中で、 どのcaseでも一致しないのであれば、 defaultのcaseが実行されます。

    ブロックしないで送受信するなら、 defaultのcaseを使ってください:

    select {
    case i := <-c:
    	// use i
    default:
    	// receiving from c would block
    }

    注意: この例は、ウェブベースのツアーでは実行できません。 サンドボックス環境は、 時間の概念がないためです。 この例を実行してみるためには、 Goのインストール が必要となるでしょう。

    package main import ( "fmt" "time" ) func main() { tick := time.Tick(1e8) boom := time.After(5e8) for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(5e7) } } }

    Exercise: Equivalent Binary Trees

    葉に保持されている値が同じ順序をもつ、 多くの種類の バイナリツリーがあります。 例えば、ここに"1, 1, 2, 3, 5, 8, 13"を保持する2つのバイナリツリーがあります。

    2つのバイナリツリーが同じ順序で保持しているかどうかを 確認するための機能は、 ほとんどの言語で非常に複雑です。 私たちは、 簡単な解決法を記述するために、 Goの並列性(concurrency)とチャネルを利用してみます。

    この例は、 型を以下のように定義する treeパッケージを利用します。

    type Tree struct {
    	Left  *Tree
    	Value int
    	Right *Tree
    }
    

    Exercise: Equivalent Binary Trees

    1. Walk関数を実装してみてください。

    2.関数をテストしてみてください。

    関数tree.New(k)は、 値(k, 2k, 3k, ..., 10k)をもつ、 ランダムに構造化されたバイナリツリーを 生成します。

    新しいチャネルchを生成し、 Walkをはじめてみましょう。

    go Walk(tree.New(1), ch)
    

    そして、そのチャネルから読み出し、10個の値を表示してみてください。 それは、 1, 2, 3, ..., 10 と表示される必要があります。

    3. t1t2が同じ値を保存しているどうかを判断するため、 Walkを使って、 Same関数を実装してみてください。

    4. Same関数をテストしてみてください。

    Same(tree.New(1), tree.New(1)) は、trueを返す必要があります。 また、 Same(tree.New(1), tree.New(2)) は、falseを返す必要があります。

    package main import "tourcode.google.com/p/go-tour/tree" // Walk walks the tree t sending all values // from the tree to the channel ch. func Walk(t *tree.Tree, ch chan int) // Same determines whether the trees // t1 and t2 contain the same values. func Same(t1, t2 *tree.Tree) bool func main() { }

    Exercise: Web Crawler

    今度の演習では、 ウェブクローラ(web crawler)を並列化するため、 Goの並列性の特徴を使います。

    同じURLを2度取ってくることなく 並行してURLを取ってくるように、 Crawl関数を 修正してみてください。

    package main import ( "fmt" ) type Fetcher interface { // Fetch returns the body of URL and // a slice of URLs found on that page. Fetch(url string) (body string, urls []string, err error) } // Crawl uses fetcher to recursively crawl // pages starting with url, to a maximum of depth. func Crawl(url string, depth int, fetcher Fetcher) { // TODO: Fetch URLs in parallel. // TODO: Don't fetch the same URL twice. // This implementation doesn't do either: if depth <= 0 { return } body, urls, err := fetcher.Fetch(url) if err != nil { fmt.Println(err) return } fmt.Printf("found: %s %q\n", url, body) for _, u := range urls { Crawl(u, depth-1, fetcher) } return } func main() { Crawl("http://golang.org/", 4, fetcher) } // fakeFetcher is Fetcher that returns canned results. type fakeFetcher map[string]*fakeResult type fakeResult struct { body string urls []string } func (f *fakeFetcher) Fetch(url string) (string, []string, error) { if res, ok := (*f)[url]; ok { return res.body, res.urls, nil } return "", nil, fmt.Errorf("not found: %s", url) } // fetcher is a populated fakeFetcher. var fetcher = &fakeFetcher{ "http://golang.org/": &fakeResult{ "The Go Programming Language", []string{ "http://golang.org/pkg/", "http://golang.org/cmd/", }, }, "http://golang.org/pkg/": &fakeResult{ "Packages", []string{ "http://golang.org/", "http://golang.org/cmd/", "http://golang.org/pkg/fmt/", "http://golang.org/pkg/os/", }, }, "http://golang.org/pkg/fmt/": &fakeResult{ "Package fmt", []string{ "http://golang.org/", "http://golang.org/pkg/", }, }, "http://golang.org/pkg/os/": &fakeResult{ "Package os", []string{ "http://golang.org/", "http://golang.org/pkg/", }, }, }

    Where to Go from here...

    Goのインストールや、 Go App Engine SDK をダウンロードすることでGo言語を始めることができます。

    Goをあなたのマシンにインストールしたら、 Goドキュメント は、 参照する 始める にはとても良いものです。 リファレンス、チュートリアル、ビデオなどなどが含まれています。

    もし、標準ライブラリに関してヘルプが必要なら、 パッケージリファレンス を見てください。 言語自身に関してヘルプには、 言語仕様 がとても読みやすいです。

    Goの並列性モデルをもっと調べてみたいなら、 Share Memory by Communicating を見てください。

    First Class Functions in Go は、Goの関数型の面白い見方を教えてくれます。

    Go Blog は、Goの有益な記事の膨大なアーカイブがあります。

    golang.org へも訪れて見てください。

    翻訳:@atotto