Hello, 世界

プログラミング言語Goツアー ( Go言語基礎文法最速マスター ) へようこそ!

このツアーには3つのセクション(基本コンセプト、メソッドとインターフェース、並行性)があります。

ツアー中には演習課題(Exercise)もありますので、修了するために挑戦してみてください。

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

ツアーにあるサンプルプログラムは、Goの特徴を示しています。 ここにあるプログラムは、Goを学習するための出発点となるでしょう。

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

それでは下にある右矢印のボタンをクリックするか、Page Downキーを押して次に進みましょう。 なお、ページの上部にある"Go"の旗でツアー全体の目次を見ることができます。

注:本サイトは、 A Tour of Go を日本語訳したものです。日本語翻訳プロジェクトはこちらです。

package main

import "fmt"

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

Go local

Goツアーは他の国の言語でも参加できます:

The tour is available in other languages:

NEXTボタンをクリックするか、Page Downキーを入力して次へ進みましょう。

The Go Playground

このツアーで実行するプログラムは、golang.org サーバ上のGo Playgroundで動いています!

このサービスはGoのプログラムをサーバで受け取り、コンパイルし、リンクし、実行し、そしてその結果を返してくれます。

ただし、playgroundで実行できるプログラムには制限があります:

  • playgroundは、標準ライブラリを使うことが出来ます。が、ネットワークアクセスやファイルアクセスは出来ません。ですので、playgroudのプログラムで使えるアウトプットは標準出力と標準エラーのみとなります。
  • playground上はいつも "2009-11-10 23:00:00 UTC" (この値の意味は、読者のために残しておきます(^^))です。これにより、同じ出力結果を得ることが容易になります。
  • 実行時のCPUとメモリ使用量の制限があり、プログラムはシングルスレッドで動くようになっています(ですが、多くのgoroutineを使えます)。

playgroundは、最終安定リリース版のGo環境を使っています。

次へ進みましょう!

package main

import (
    "fmt"
    "net"
    "os"
    "time"
)

func main() {
    fmt.Println("Welcome to the playground!")

    fmt.Println("The time is", time.Now())

    fmt.Println("And if you try to open a file:")
    fmt.Println(os.Open("filename"))

    fmt.Println("Or access the network:")
    fmt.Println(net.Dial("tcp", "google.com"))
}

Packages

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

プログラムの処理は main パッケージ内で始まります。

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

規約で、パッケージ名はインポートパスの最後の要素と同じになります。 例えば、 "math/rand" パッケージは package rand ステートメントで始まるファイル群で構成されます。

(訳注:もしインポートパスが "code.google.com/p/go.net/websocket" だった場合は、 websocket になります)

注意: プログラムが実行されるplaygroundの環境は、いつも同じ状態です。 ですので、擬似乱数を返す rand.Intn はいつも同じ数を返します。 (数を強制的に変えるなら、乱数生成でシードを与える必要があります。rand.Seedを見てみてください。)

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10))
}

Imports

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

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 continued

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

Multiple results

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

この 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)
}

Named results

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

もし、戻り値パラメータに名前が付けられているなら、 return ステートメントに戻り値の変数名を書く必要はありません。 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 ステートメントは変数を宣言します。 関数の引数リストと同じように、2つ以上連続した 関数パラメータの最後に型を書くことで、変数のリストを宣言できます。

package main

import "fmt"

var i int
var c, python, java bool

func main() {
    fmt.Println(i, c, python, java)
}

Variables with initializers

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

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

package main

import "fmt"

var i, j int = 1, 2
var c, python, java = true, false, "no!"

func main() {
    fmt.Println(i, j, c, python, java)
}

Short variable declarations

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

なお、関数外でのすべての宣言にはキーワードでの宣言(`var`, func, など)が必要で、 := での暗黙的な宣言は利用できません。

package main

import "fmt"

func main() {
    var i, j int = 1, 2
    k := 3
    c, python, java := true, false, "no!"

    fmt.Println(i, j, k, c, python, java)
}

Basic types

Go言語の基本型(組み込み型)はこれです:

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 の別名

rune // int32 の別名
     // Unicode のコードポイントを表す

float32 float64

complex64 complex128

(訳注:runeとは古代文字を表す言葉(runes)ですが、Goでは文字そのものを表すためにruneという言葉を使います。)

package main

import (
    "fmt"
    "math/cmplx"
)

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

Constants

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

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

なお、定数は := を使って宣言できません。

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

数値定数は高精度な ( 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 ループだけを繰り返し文として使います。 Goには while 文はありません!

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

package main

import "fmt"

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

For continued

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

package main

import "fmt"

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

For is Go's "while"

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

package main

import "fmt"

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

Forever

ループ条件を省略すれば、無限ループになりますので、無限ループをコンパクトに表現できます。

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 with a short statement

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

ここで宣言された変数は、 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 and else

ちなみに 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),
    )
}

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

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リテラルは、フィールドの値を列挙することによって、構造体の初期値の割り当てを示しています。

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

訳注:例では X: 1 として X だけを初期化しています。

特別な接頭辞 & は、新しく割り当てられた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)
}

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

Slicing 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:])
}

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

Nil 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!")
    }
}

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 continued

インデックスや値を " _ "(アンダーバー) へ代入することで破棄することができます。

もしインデックスだけが必要なら、例のコードで ", 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)
    }
}

Exercise: Slices

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

生成する画像は、好きに選んでください。面白い関数の例は、 x^y(x+y)/2x*y などがあります。

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

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

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

package main

import "code.google.com/p/go-tour/pic"

func Pic(dx, dy int) [][]uint8 {
}

func main() {
    pic.Show(Pic)
}

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

Map literals

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

Map literals continued

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

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

Mutating Maps

map m へ要素(elem)の挿入や更新は:

m[key] = elem

要素の取り出しは:

elem = m[key]

要素の削除は:

delete(m, key)

キーが存在するかどうかは、2つの値の代入で確認します:

elem, ok = m[key]

もし、map mkey があれば、 oktrue になります。 なければ、 okfalse となり、`elem` は、mapの要素の型のゼロの値となります。

同様に、mapにkeyが存在しない場合に読み込んだ結果も、mapの要素の型のゼロの値となります。

package main

import "fmt"

func main() {
    m := make(map[string]int)

    m["Answer"] = 42
    fmt.Println("The value:", m["Answer"])

    m["Answer"] = 48
    fmt.Println("The value:", m["Answer"])

    delete(m, "Answer")
    fmt.Println("The value:", m["Answer"])

    v, ok := m["Answer"]
    fmt.Println("The value:", v, "Present?", ok)
}

Exercise: Maps

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

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

package main

import (
    "code.google.com/p/go-tour/wc"
)

func WordCount(s string) map[string]int {
    return map[string]int{"x": 1}
}

func main() {
    wc.Test(WordCount)
}

Function values

関数も値で扱えます。

つまり、関数そのものを変数に入れることもできるということです。

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

Function closures

Goの関数は クロージャ( closure ) です。 クロージャは、それ自身の外部から変数を参照する関数の値( function value )です。 この関数は、参照された変数へアクセスして変えることができ、その意味では、その関数は変数へ"バインド"されています。

例えば、 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),
        )
    }
}

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

Switch

みなさんは、 switch がどのようなものだったかを知っていたでしょうか。 Goは違います。

Goでのswitchは、 case の最後で自動的にbreakします。 もし、brakeせずに通したい場合は、 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 evaluation order

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

(例えば、

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

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

注意:Go playgroundでの時間はいつも 2009-11-10 23:00:00 UTC で始まります。 この値の意味は、読者の演習のために残しておきます。

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 with no condition

条件のない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.")
    }
}

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

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 continued

実際には、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 の場合は、何の影響もありません。 Scale は`v`を変化させます。 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 )は、メソッド群で定義されます。

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

注意: このコードのコンパイルは失敗します。

Abs メソッドが、 Vertex ではなく *Vertex の定義であり、 VertexAbser を満たしていないということになるためエラーとなります。

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

    // In the following line, v is a Vertex (not *Vertex)
    // and does NOT implement Abser.
    a = v

    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 are satisfied implicitly

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

インターフェースを実装することを明示的に宣言する必要はありません。

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

これはまた、明瞭なインターフェースの定義づけを促します。 なぜなら、すべての実装を見つける必要はありませんし、新しいインターフェースの名前でそれを関連付ける必要もないからです。

io パッケージは、ここで定義したインターフェース ReaderWriter を定義しています。 ですので、何もしなくて大丈夫です。

(訳注:fmt.Fprint関数の第一引数はio.Writerタイプであり、この例で定義したWriterと同じインターフェースをもっています。Goではインターフェースを実装していることを明示する必要がありませんので、このようなことが簡単に実現できます。)

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

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

type error interface {
    Error() string
}

fmt パッケージのさまざまな表示ルーチンは、 error の表示を求められたときに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)
    }
}

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

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

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

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: 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 (
    "code.google.com/p/go-tour/pic"
    "image"
)

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

次の章では、Goの並行性( concurrency )について学びます。

Goroutines

goroutine (ゴルーチン)は、Goのランタイムに管理される軽量なスレッドです。

go f(x, y, z)

と書けば、 f は新しいgoroutine上で実行されます。

f(x, y, z)

f 、`x y`、 z の評価は、現在のgoroutineで発生し、 f の実行は、新しいgoroutineで発生します。

goroutineは、同じアドレス空間で実行されるため、共有メモリへのアクセスは、きちんと同期する必要があります。 sync パッケージは役に立つ方法を提供していますが、他の方法があるので、Goではあまり必要ありません。 (次のスライドをみてください。)

package main

import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("world")
    say("hello")
}

Channels

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

ch <- v    // v をチャネル ch へ送る。
v := <-ch  // ch から受信し、
           // 変数を 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

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

ch := make(chan int, 100)

バッファがいっぱいになる時だけ、バッファ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 := 0, 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 := 0, 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 にも一致しないのであれば、 defaultcase が実行されます。

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

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

import (
    "fmt"
    "time"
)

func main() {
    tick := time.Tick(100 * time.Millisecond)
    boom := time.After(500 * time.Millisecond)
    for {
        select {
        case <-tick:
            fmt.Println("tick.")
        case <-boom:
            fmt.Println("BOOM!")
            return
        default:
            fmt.Println("    .")
            time.Sleep(50 * time.Millisecond)
        }
    }
}

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 "code.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 関数を修正してみてください。(訳注1)

訳注1: 工夫すれば 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のパッケージの構成方法や動かし方を学ぶためには この動画 を見てみてください。また、 How to Write Go Code を読んでみてください。

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

Goの並行性のモデルについてもっと調べてみたいなら、 Go Concurrency Patterns (slides) や、 Advanced Go Concurrency Patterns (slides) を見たり、 codewalk: Share Memory by Communicating を読んでみてください。

Webアプリケーションをはじめてみるなら、 A simple programming environment (slides) を見たり、 チュートリアル: Writing Web Applications を読んでみてください。

First Class Functions in Go では、Goの関数型の興味深い側面を教えてくれます。

Go Blog には、Goに関する素晴らしい記事の膨大なアーカイブがあります。

もちろん、 golang.org も見てみてください。

翻訳: @atotto