標準入出力

ファイル入出力はファイルディスクリプタを介して行う。要するにファイルをオープンしてファイルディスクリプタを取得し、使い終わったらクローズする、ってわけだ。
ただし、標準入出力に関しては最初から os パッケージに用意されている。

  • os.Stdin: 標準入力
  • os.Stdout: 標準出力
  • os.Stderr: 標準エラー出力

標準入力から読み込むには os.Stdin.Read 関数、出力するには os.Stdout.Write または os.Stderr.Write 関数を使う。
ちょっと試してみよう。次のプログラムは、標準入力から受け取った文字列をそのまま標準出力に書き出す(ctrl + C で終了)。

^o^ > go run echo.go
hello
hello

ひとつめの hello がキーボードからの入力で、ふたつめの hello が返ってきた出力だ。

二分探索木

今日は、練習のため二分探索木の写経をする。

 cf. 二分探索木 – M.Hiroi’s Home Page お気楽 Go 言語プログラミング入門

探索木自体は Item 型(インターフェイス)を対象としている。このため intInt という別名をつけて EqLess の2つのメソッドを定義している。わざわざ別名をつけているのは、通常の(構造体でない)型にはメソッドを定義できないため。

^o^ > go run binarytree.go
0 1 2 3 4 5 6 7 8 9
true
true
true
true
true
true
true
true
true
true
0 1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
2 3 4 5 6 7 8 9
3 4 5 6 7 8 9
4 5 6 7 8 9
5 6 7 8 9
6 7 8 9
7 8 9
8 9
9

インターフェイスの埋め込み

構造体と同じく、インターフェイスも別のインターフェイスに埋め込むことができる。
下の例では、BazIFooIBarI を埋め込んでいる。すると、BazI を実装した構造体 Baz では、FooIBarI のメソッドを、あたかも自分のメソッドのように使えるようになる。

^o^ > go run interface_embed.go
1
10
0
0
2
20
1
2
0
0
3
4

型switch

データ型の判定は switch 文でもできる。これを型switchという。一般的な書式はこう。

一昨日書いた型アサーションと似ているけど、カッコの中には type と書く。すると対応する型の case 節が実行される。

^o^ > go run type_switch.go
6 6.6

空インターフェイスと型アサーション

空インターフェイスとは、定義すべきメソッドを持たないインターフェイスのことだ。言い換えるとどんな型にも当てはまる。空インターフェイスの配列やスライスはどんな型でも格納することができる。下の例ではスライス a に整数、実数、文字列を格納している。
Go は静的型付け言語なので、スライスに格納されているのが何の型なのかわからないのでは都合が悪い。そこで、型をチェックする機能がある。それが型アサーションだ。型アサーションは「変数.(型)」という書き方をして、変数が型と一致すればその値と true の2つの値が返ってくる。一致しなければゼロ値と false だ。下の例では、この型アサーションを使って、スライスに含まれる整数と実数のそれぞれの合計を求めている。

^o^ > go run interface3.go
6
6.6

Perlの配列をn個ずつの配列に分割する

splice して push する。破壊的なので注意。

^o^ > perl chunk.pl
array:0123456789
chunk:012
chunk:345
chunk:678
chunk:9
array:

インターフェイス

インターフェイスとは、異なる型の構造体を同じ型として扱うための抽象型のようなものだ。Java のインターフェイスと同様らしいけど Java のことはよく知らない。インターフェイスにはメソッドの型だけが定義されていて、そのメソッドすべてを実装した構造体はインターフェイスの型として扱えるようになる。

次の例では、インターフェイス Point を定義している。Point 型は distance0 というメソッドを持っている。Point2dPoint3d というのが具体的な構造体で、そのどちらもが distance0 メソッドを実装しているので、Point 型として1つのスライスに格納されている。

^o^ > go run interface.go
0
14.142135623730951
0
17.320508075688775
31.462643699419726

構造体の埋め込み

Go はオブジェクト指向言語ではないけど、「構造体の埋め込み」という機能によって、似たようなことができる。
例を見てみよう。構造体 Bar にはフィールド foo として構造体 Foo が指定されている。これは単に、フィールドが構造体なだけであって、埋め込みとは言わない。一方、構造体 Baz にはフィールド名を省略して Foo が指定されている。これを匿名フィールドと言って、Baz には Foo のフィールドやメソッドが引き継がれる。これを構造体の埋め込みという。

変数 y は構造体 Bar で、Foo を埋め込んでいないので、y.printA() のようなメソッド呼び出しはできない。一方、変数 zFoo を埋め込んでいるので、z.printA() のように Foo のメソッドを呼び出すことができる。

o^ > go run struct_embed.go
a = 1
b = 2
a = 10
b = 20
c = 30
a = 100
Baz:b = 200
c = 300

複数の構造体を埋め込むこともできる。ただし、埋め込んだ構造体同士でフィールド名やメソッド名が重複すると、やや面倒になる。次の例では、構造体 Baz には FooBar が埋め込んであるけど、フィールド a とメソッド printA が重複している。こういう時は構造体名を明示して呼び出さなければならない。

^o^ > go run struct_embed2.go
0
20
30

連結リスト

連結リストとは、値を保持するセルが数珠つなぎになった構造、つまり Scheme のリストと同じ構造だ。
今日は構造体やメソッドの練習として、連結リストのプログラムを写経してみる。
書き忘れていたけど、Go の一連の勉強は↓このページに沿ってやっている。

 cf. お気楽 Go 言語プログラミング入門 – M.Hiroi’s Home Page

連結リストがあるのはこのページの中ほど。

 cf. お気楽 Go 言語プログラミング入門 構造体 – M.Hiroi’s Home Page

それじゃ、行ってみよう。

それほど難しくはない。ちょっと注意が必要なのは、List 構造体の top フィールドに入っている Cell 構造体はダミーで、その次の Cell が0番目の Cell になってるってところかな。
実行結果:

^o^ > go run linkedlist.go
true
true
true
true
0 1 2 3
0 true
1 true
2 true
3 true
0 false
1 2 3
2 3
3

うまくいった。

メソッド

メソッドは、構造体(の型)と結びつけられた関数だ。Go はオブジェクト指向プログラミング言語ではないけど、このメソッドや構造体の埋め込み(これについては後で書くつもり)によって、オブジェクト指向的なプログラミングができるようになっている。
例を挙げよう。
前のエントリでは平面上の点を表す Point 構造体と、2点間の距離を求める distance 関数を作った。ここでもう一つ、3次元空間の点を表す Point3d 構造体を作る。この2点間の距離を求める関数を考えたとき、distance という名前は使えない。すでに名前を使ってしまっているからだ。
ところが、これらをそれぞれの構造体のメソッドにすれば、両方とも distance という名前にできる。
メソッドの一般亭な定義は次の通り。

ここで、関数名の前のカッコの中にある仮引数をレシーバと言い、このメソッドはレシーバの型と結びつけられる。これで、PointPoint3d の両方の構造体に、同じ distance という名前のメソッドが定義できるわけだ。
実際に書いてみよう。

^o^ > go run method.go
14.142135623730951
17.320508075688775