プログラミング言語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ツアーは他の国の言語でも参加できます:
The tour is available in other languages:
NEXTボタンをクリックするか、Page Downキーを入力して次へ進みましょう。
このツアーで実行するプログラムは、golang.org サーバ上のGo Playgroundで動いています!
このサービスはGoのプログラムをサーバで受け取り、コンパイルし、リンクし、実行し、そしてその結果を返してくれます。
ただし、playgroundで実行できるプログラムには制限があります:
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")) }
すべての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)) }
例のコードは、括弧でインポートをグループ化し、要素化した( factored )インポートステートメントとしています。 もちろん、複数のインポートステートメントを用いて以下のように書くこともできます:
import "fmt" import "math"
package main import ( "fmt" "math" ) func main() { fmt.Printf("Now you have %g problems.", math.Nextafter(2, 3)) }
パッケージをインポートすると、そのパッケージが外部に公開(エクスポート)している名前を参照することができます。
Goでは、最初の文字が大文字で始まる場合は、その名前はエクスポートされています。
たとえば、 Pi
は外部へ公開される名前ですが、 pi
では公開されません。
コードを実行してみてください。そして、 math.pi
を math.Pi
に書き換えて、もう一度実行してみてください。
エクスポートされた関数を使うことができましたか?
package main import ( "fmt" "math" ) func main() { fmt.Println(math.pi) }
関数は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)) }
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)) }
関数は複数の戻り値を返すことができます。
この 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) }
関数はパラメータを取ることができます。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)) }
var
ステートメントは変数を宣言します。
関数の引数リストと同じように、2つ以上連続した
関数パラメータの最後に型を書くことで、変数のリストを宣言できます。
package main import "fmt" var i int var c, python, java bool func main() { fmt.Println(i, c, python, java) }
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) }
関数内では、 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) }
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) }
定数( 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) }
数値定数は高精度な 値 ( 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)) }
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) }
C言語 や Java のように、条件の前後を空にすることができます。
package main import "fmt" func main() { sum := 1 for ; sum < 1000; { sum += sum } fmt.Println(sum) }
セミコロンを抜くこともできます。つまり、C言語での while
は、Goでは for
だけを使います。
package main import "fmt" func main() { sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum) }
ループ条件を省略すれば、無限ループになりますので、無限ループをコンパクトに表現できます。
package main func main() { for { } }
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
のスコープだけで有効です。
(ためしに最後の 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
ステートメントで宣言された変数は、 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), ) }
関数とループを使った簡単な練習として、 ニュートン法 を使った平方根の計算を実装してみましょう。
この問題では、ニュートン法は、
開始点 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)) }
struct
(構造体)は、フィールド( field )の集まりです。
(また、 type
宣言は皆さんが知っているものです。)
package main import "fmt" type Vertex struct { X int Y int } func main() { fmt.Println(Vertex{1, 2}) }
構造体のフィールドは、ドット(.)を用いてアクセスします。
package main import "fmt" type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) }
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リテラルは、フィールドの値を列挙することによって、構造体の初期値の割り当てを示しています。
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) }
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) }
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]) } }
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:]) }
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) }
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!") } }
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) } }
インデックスや値を " _
"(アンダーバー) へ代入することで破棄することができます。
もしインデックスだけが必要なら、例のコードで ", 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) } }
Pic
関数を実装してみましょう。
このプログラムを実行すると、生成した画像が下に表示されるはずです。
この関数は、長さ dy
のsliceに、各要素が8bitのunsigned int型で長さ dx
のsliceを割り当てたものを返す必要があります。
画像は、整数値をグレースケール(実際はブルースケール)として解釈したものです。
生成する画像は、好きに選んでください。面白い関数の例は、 x^y
、 (x+y)/2
、 x*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) }
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リテラルは、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) }
もし、トップレベルの型が型名である場合は、リテラルの要素から推定できますので、その型名を省略することができます。
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) }
map m
へ要素(elem)の挿入や更新は:
m[key] = elem
要素の取り出しは:
elem = m[key]
要素の削除は:
delete(m, key)
キーが存在するかどうかは、2つの値の代入で確認します:
elem, ok = m[key]
もし、map m
に key
があれば、 ok
は true
になります。
なければ、 ok
は false
となり、`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) }
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) }
関数も値で扱えます。
つまり、関数そのものを変数に入れることもできるということです。
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)) }
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), ) } }
関数で面白いものを見てみましょう。
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
がどのようなものだったかを知っていたでしょうか。
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 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は、 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.") } }
complex64
と complex128
の型による、
Go言語の複素数( complex )について見てみましょう。
立方根( cube root )の場合、ニュートン法は、以下の式を繰り返すことになります:
"2"の立方根を探し、アルゴリズムが正しく動作するか確認してください。
math/cmplx
パッケージに Pow
関数がありますので、結果を確認してみましょう。
package main import "fmt" func Cbrt(x complex128) complex128 { } func main() { fmt.Println(Cbrt(2)) }
次の章では、メソッドとインターフェースが登場し、オブジェクトとその動作を定義する方法について学びます。
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()) }
実際には、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()) }
メソッドは、名前付きの型( named type )や、名前付きの型へのポインタに関連付けられます。
ここまでで、2つの Abs
関数がありました。
ひとつは、 *Vertex
のポインタ型( pointer type )で、
もうひとつは、 MyFloat
の値型( value type )です。
ポインタのレシーバ( pointer receiver )を使う2つの理由があります。 1つ目は、各メソッド呼び出しで、値をコピーするのを避けるためです。 (最も効果的なのは、値型が大きな構造体の場合です) 2つ目は、そのメソッドは、そのレシーバが指している値を変更できるようになります。
Abs
と Scale
メソッドの定義を、
*Vertex
の代わりに、レシーバとして Vertex
を使って書き換えてみましょう。
Scale
メソッドは、 v
が Vertex
の場合は、何の影響もありません。
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()) }
インターフェース型( interface type )は、メソッド群で定義されます。
インターフェース型の値は、それらのメソッドを実装する任意の値をもつことができます。
注意: このコードのコンパイルは失敗します。
Abs
メソッドが、 Vertex
ではなく *Vertex
の定義であり、
Vertex
が Abser
を満たしていないということになるためエラーとなります。
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) }
ある型に、メソッドを実装することで、インターフェースを実装します。
インターフェースを実装することを明示的に宣言する必要はありません。
この暗黙的なインターフェースは、インターフェースを定義するパッケージと、実装されているパッケージとを分離することができます。 (他に依存しません)
これはまた、明瞭なインターフェースの定義づけを促します。 なぜなら、すべての実装を見つける必要はありませんし、新しいインターフェースの名前でそれを関連付ける必要もないからです。
io
パッケージは、ここで定義したインターフェース Reader
と Writer
を定義しています。
ですので、何もしなくて大丈夫です。
(訳注: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") }
エラーは、エラー文字列としてエラーそのものを説明できるものです。
このしくみは、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) } }
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)) }
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) }
以下の型を実装し、それらに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) }
image
パッケージは、以下の Image
インターフェースを定義しています:
package image type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color }
(詳細は、 このドキュメント を参照してください。)
また、 color.Color
と color.Model
は共にインターフェースです。
しかし、定義済みの color.RGBA
と color.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()) }
前に書いた、画像ジェネレーターを覚えてますか?
他のものを書いてみましょう。でも今回は、データのスライスの代わりに 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) }
ある共通のパターンは、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) }
次の章では、Goの並行性( concurrency )について学びます。
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") }
チャネル( 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) }
チャネルは、 バッファ できます。
バッファ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) }
送り手は、これ以上の送信する値がないことを示すため、チャネルを 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
ステートメントは、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) }
select
の中で、どの case
にも一致しないのであれば、 default
の case
が実行されます。
ブロックしないで送受信するなら、 default
の case
を使ってください:
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) } } }
葉に保持されている値が同じ順序をもつ、多くの種類のバイナリツリーがあります。 例えば、ここに"1, 1, 2, 3, 5, 8, 13"を保持する2つのバイナリツリーがあります。
2つのバイナリツリーが同じ順序で保持しているかどうかを確認する機能は、多くの他の言語でとても複雑です。 私たちは簡単な解決法を記述するために、Goの並行性( concurrency )とチャネルを利用してみます。
この例は、型を以下のように定義する tree
パッケージを利用します。
type Tree struct { Left *Tree Value int Right *Tree }
1. Walk
関数を実装してみてください。
2. 関数をテストしてみてください。
関数 tree.New(k)
は、値( k
, 2k
, 3k
, ..., 10k
)をもつ、ランダムに構造化されたバイナリツリーを生成します。
新しいチャネル ch
を生成し、 Walk
をはじめてみましょう。
go Walk(tree.New(1), ch)
そして、そのチャネルから読み出し、10個の値を表示してみてください。 それは、 1, 2, 3, ..., 10 という表示になるでしょう。
3. t1
と t2
が同じ値を保存しているどうかを判断するため、 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() { }
今度の演習では、ウェブクローラ( 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/", }, }, }
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