標準入出力

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

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

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

package main

import "os"

func main() {
    buff := make([]byte, 256)
    for {
        c, _ := os.Stdin.Read(buff)
        if c == 0 { break }
        os.Stdout.Write(buff[:c])
    }
}
^o^ > go run echo.go
hello
hello

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

二分探索木

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

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

package main

import "fmt"

type Item interface {
    Eq(Item) bool
    Less(Item) bool
}

type Node struct {
    item Item
    left, right *Node
}

func newNode(x Item) *Node {
    p := new(Node)
    p.item = x
    return p
}

func searchNode(node *Node, x Item) bool {
    for node != nil {
        switch {
            case x.Eq(node.item): return true
            case x.Less(node.item): node = node.left
            default: node = node.right
        }
    }
    return false
}

func insertNode(node *Node, x Item) *Node {
    switch {
        case node == nil: return newNode(x)
        case x.Eq(node.item): return node
        case x.Less(node.item):
            node.left = insertNode(node.left, x)
        default:
            node.right = insertNode(node.right, x)
    }
    return node
}

func searchMin(node *Node) Item {
    if node.left == nil {
        return node.item
    }
    return searchMin(node.left)
}

func deleteMin(node *Node) *Node {
    if node.left == nil {
        return node.right
    }
    node.left = deleteMin(node.left)
    return node
}

func deleteNode(node *Node, x Item) *Node {
    if node != nil {
        if x.Eq(node.item) {
            if node.left == nil {
                return node.right
            } else if node.right == nil {
                return node.left
            } else {
                node.item = searchMin(node.right)
                node.right = deleteMin(node.right)
            }
        } else if x.Less(node.item) {
            node.left = deleteNode(node.left, x)
        } else {
            node.right = deleteNode(node.right, x)
        }
    }
    return node
}

func foreachNode(f func(Item), node *Node) {
    if node != nil {
        foreachNode(f, node.left)
        f(node.item)
        foreachNode(f, node.right)
    }
}

type Tree struct {
    root *Node
}

func newTree() *Tree {
    return new(Tree)
}

func (t *Tree) searchTree(x Item) bool {
    return searchNode(t.root, x)
}

func (t *Tree) insertTree(x Item) {
    t.root = insertNode(t.root, x)
}

func (t *Tree) deleteTree(x Item) {
    t.root = deleteNode(t.root, x)
}

func (t *Tree) foreachTree(f func(Item)) {
    foreachNode(f, t.root)
}

func (t *Tree) printTree() {
    t.foreachTree(func(x Item) { fmt.Print(x, " ") })
    fmt.Println("")
}

type Int int

func (n Int) Eq(m Item) bool {
    return n == m.(Int)
}

func (n Int) Less(m Item) bool {
    return n < m.(Int)
}

func main() {
    a := newTree()
    for _, v := range []int { 5,6,4,7,3,8,2,9,1,0 } {
        a.insertTree(Int(v))
    }
    a.printTree()
    for i := 0; i < 10; i++ {
        fmt.Println(a.searchTree(Int(i)))
    }
    a.printTree()
    for i := 0; i < 10; i++ {
        a.deleteTree(Int(i)) a.printTree()
    }
}

探索木自体は 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 のメソッドを、あたかも自分のメソッドのように使えるようになる。

package main

import "fmt"

type Foo struct {
    a int
}

type FooI interface {
    getA() int
}

func (p *Foo) getA() int {
    return p.a
}

type Bar struct {
    b int
}

type BarI interface {
    getB() int
}

func (p *Bar) getB() int {
    return p.b
}

type Baz struct {
    Foo
    Bar
}

type BazI interface {
    FooI
    BarI
}

func main() {
    a := []FooI{
        &Foo{1},
        &Foo{2},
        &Baz{},
    }
    b := []BarI{
        &Bar{10},
        &Bar{20},
        &Baz{},
    }
    c := []BazI{
        &Baz{},
        &Baz{Foo{1}, Bar{2}},
        &Baz{Foo{3}, Bar{4}},
    }
    for i := 0; i < 3; i++ {
        fmt.Println(a[i].getA())
        fmt.Println(b[i].getB())
        fmt.Println(c[i].getA())
        fmt.Println(c[i].getB())
    }
}
^o^ > go run interface_embed.go
1
10
0
0
2
20
1
2
0
0
3
4

型switch

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

switch v := x.(type) {
    case 型1: 処理
    case 型2: 処理
    ...
    default: 処理
}

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

package main

import "fmt"

type Num interface {
    number()
}

type Int int

func (n Int) number() {}

type Real float64

func (n Real) number() {}

func sumOfNum(ary []Num) (Int, Real) {
    var sumi Int = 0
    var sumr Real = 0.0
    for _, x := range ary {
        switch v := x.(type) {
            case Int: sumi += v
            case Real: sumr += v
       }
    }
    return sumi, sumr
}

func main() {
    var ary[]Num = []Num{
        Int(1),
        Real(1.1),
        Int(2),
        Real(2.2),
        Int(3),
        Real(3.3),
    }
    a, b := sumOfNum(ary)
    fmt.Println(a, b)
}
^o^ > go run type_switch.go
6 6.6

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

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

package main

import "fmt"

func sumOfInt(ary []interface{}) int {
    sum := 0
    for _, x := range ary {
        v, ok := x.(int)
        if ok {
            sum += v
        }
    }
    return sum
}

func sumOfFloat(ary []interface{}) float64 {
    sum := 0.0
    for _, x := range ary {
        v, ok := x.(float64)
        if ok {
            sum += v
        }
    }
    return sum
}

func main() {
    a := []interface{}{ 1, 1.1, "abc", 2, 2.2, "def", 3, 3.3 }
    fmt.Println(sumOfInt(a))
    fmt.Println(sumOfFloat(a))
}
^o^ > go run interface3.go
6
6.6

インターフェイス

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

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

package main

import (
    "fmt"
    "math"
)

type Point interface {
    distance0() float64
}

type Point2d struct {
    x, y float64
}

func newPoint2d(x, y float64) *Point2d {
    p := new(Point2d)
    p.x, p.y = x, y
    return p
}

func (p *Point2d) distance0() float64 {
    return math.Sqrt(p.x * p.x + p.y * p.y)
}

type Point3d struct {
    x, y, z float64
}

func newPoint3d(x, y, z float64) *Point3d {
    p := new(Point3d)
    p.x, p.y, p.z = x, y, z
    return p
}

func (p *Point3d) distance0() float64 {
    return math.Sqrt(p.x * p.x + p.y * p.y + p.z * p.z)
}

func sumOfDistance0(ary []Point) float64 {
    sum := 0.0
    for _, p := range ary {
        sum += p.distance0()
    }
    return sum
}

func main() {
    a := []Point{
        newPoint2d(0, 0),
        newPoint2d(10, 10),
        newPoint3d(0, 0, 0),
        newPoint3d(10, 10, 10),
    }

    fmt.Println(a[0].distance0())
    fmt.Println(a[1].distance0())
    fmt.Println(a[2].distance0())
    fmt.Println(a[3].distance0())
    fmt.Println(sumOfDistance0(a))
}
^o^ > go run interface.go
0
14.142135623730951
0
17.320508075688775
31.462643699419726

構造体の埋め込み

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

package main

import "fmt"

type Foo struct {
    a, b int
}

func (x Foo) printA() {
    fmt.Println("a =", x.a)
}

func (x Foo) printB() {
    fmt.Println("b =", x.b)
}

type Bar struct {
    foo Foo
    c int
}

func (x Bar) printC() {
    fmt.Println("c =", x.c)
}

type Baz struct {
    Foo
    c int
}

func (x Baz) printB() {
    fmt.Print("Baz:")
    x.Foo.printB()
}

func (x Baz) printC() {
    fmt.Println("c =", x.c)
}

func main() {
    x := Foo{ 1,2 }
    y := Bar{ Foo{ 10,20 }, 30 }
    z := Baz{ Foo{ 100,200 }, 300 }

    x.printA()
    x.printB()
    y.foo.printA()
    // y.printA() エラー
    y.foo.printB()
    // y.printB() エラー
    y.printC()
    z.printA()
    z.printB()
    z.printC()
}

変数 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 が重複している。こういう時は構造体名を明示して呼び出さなければならない。

package main

import "fmt"

type Foo struct {
    a, b int
}

func (p *Foo) printA() {
    fmt.Println(p.a)
}

func (p *Foo) printB() {
    fmt.Println(p.b)
}

type Bar struct {
    a, c int
}

func (p *Bar) printA() {
    fmt.Println(p.a)
}

func (p *Bar) printC() {
    fmt.Println(p.c)
}

type Baz struct {
    Foo
    Bar
}

func main() {
    x := new(Baz)
    x.Bar.a = 10
    x.b = 20
    x.c = 30
    x.Foo.printA()
    x.printB()
    x.printC()
}
^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

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

package main

import (
    "fmt"
)

type Cell struct {
    item int
    next *Cell
}

type List struct {
    top *Cell
}

func newCell(x int, cp *Cell) *Cell {
    c := new(Cell)
    c.item, c.next = x, cp
    return c
}

func newList() *List {
    lst := new(List)
    lst.top = new(Cell)
    return lst
}

func (cp *Cell) nthCell(n int) *Cell {
    i := -1
    for cp != nil {
        if i == n { return cp }
        i++
        cp = cp.next
    }
    return nil
}

func (lst *List) nth(n int) (int, bool) {
    cp := lst.top.nthCell(n)
    if cp == nil {
        return 0, false
    } else {
        return cp.item, true
    }
}

func (lst *List) insertNth(n, x int) bool {
    cp := lst.top.nthCell(n - 1)
    if cp == nil {
        return false
    } else {
        cp.next = newCell(x, cp.next)
        return true
    }
}

func (lst *List) deleteNth(n int) bool {
    cp := lst.top.nthCell(n - 1)
    if cp == nil || cp.next == nil {
        return false
    } else {
        cp.next = cp.next.next
        return true
    }
}

func (lst *List) isEmpty() bool {
    return lst.top.next == nil
}

func (lst *List) printList() {
    cp := lst.top.next
    for ; cp != nil; cp = cp.next {
        fmt.Print(cp.item, " ")
    }
    fmt.Println("")
}

func main() {
    a := newList()
    for i := 0; i < 4; i++ {
        fmt.Println(a.insertNth(i, i))
    }
    a.printList()
    for i := 0; i < 5; i++ {
        n, ok := a.nth(i)
        fmt.Println(n, ok)
    }
    for !a.isEmpty() {
        a.deleteNth(0)
        a.printList()
    }
}

それほど難しくはない。ちょっと注意が必要なのは、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 という名前にできる。
メソッドの一般亭な定義は次の通り。

func (仮引数 *型) 関数名(仮引数, ...) {
    処理
}

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

package main

import (
    "fmt"
    "math"
)

type Point struct {
    x, y float64
}

type Point3d struct {
    x, y, z float64
}

func newPoint(x, y float64) *Point {
    p := new(Point)
    p.x, p.y = x, y
    return p
}

func newPoint3d(x, y, z float64) *Point3d {
    p := new(Point3d)
    p.x, p.y, p.z = x, y, z
    return p
}

func (p *Point) distance(q *Point) float64 {
    dx := p.x - q.x
    dy := p.y - q.y
    return math.Sqrt(dx * dx + dy * dy)
}

func (p *Point3d) distance(q *Point3d) float64 {
    dx := p.x - q.x
    dy := p.y - q.y
    dz := p.z - q.z
    return math.Sqrt(dx * dx + dy * dy + dz * dz)
}

func main() {
    p1 := newPoint(0.0, 0.0)
    p2 := newPoint(10.0, 10.0)
    q1 := newPoint3d(0.0, 0.0, 0.0)
    q2 := newPoint3d(10.0, 10.0, 10.0)

    fmt.Println(p1.distance(p2))
    fmt.Println(q1.distance(q2))
}
^o^ > go run method.go
14.142135623730951
17.320508075688775

構造体

構造体は、既存の型を組み合わせて新しい型を作る機能だ。とりあえずは C の構造体を同じようなものだと考えればいい。
構造体の定義は type キーワードを使って次のようにする。

type 型名 struct {
    フィールド1 型1
    フィールド2 型2
    ...
}

構造体を構成するデータは、フィールドと呼ばれる。上のようにフィールドとその型を列記すればいい。

構造体の初期化は、構造体名{フィールド1, フィールド2, …} のようにフィールドを順に並べるほか、フィールド名とそのデータを組みにして渡す方法がある。後者では定義と順番が異なってもいい。また、フィールドへのアクセスは . 演算子を使う。例を示そう。

package main

import (
    "fmt"
    "math"
)

type Point struct {
    x float64
    y float64
}

func distance(p, q Point) float64 {
    dx := p.x - q.x
    dy := p.y - q.y
    return math.Sqrt(dx * dx + dy * dy)
}

func main() {
    var o Point
    var p Point = Point{ 10.0, 10.0 }
    var q Point = Point{ y: 200.0, x: 100.0 }

    fmt.Println(o)
    fmt.Println(p)
    fmt.Println(q)

    fmt.Println(o.x)
    fmt.Println(o.y)
    fmt.Println(p.x)
    fmt.Println(p.y)
    fmt.Println(q.x)
    fmt.Println(q.y)

    fmt.Println(distance(o, p))
    fmt.Println(distance(o, q))
    fmt.Println(distance(p, q))
}

ここでは Point という名前の構造体を使っている。構造体の初期化は、main 関数の冒頭で行っている。変数 pq は上に書いたように初期化している。変数 o は初期化していないので、自動的にゼロ値に初期化される。
distance 関数の中で、構造体のフィールドにアクセスしている。

^o^ > go run struct.go
{0 0}
{10 10}
{100 200}
0
0
10
10
100
200
14.142135623730951
223.60679774997897
210.23796041628637

次は、構造体のポインタの例を示そう。構造体の場合も普通の型のように、アドレスを取得するには & を、値にアクセスするには * を使えばいい。ただし、フィールドにアクセスするときは、C のように -> を使うのではなく、. を使う。このへんは構造体の変数なのかポインタなのかを気にしなくていいので楽だな。

package main

import (
    "fmt"
    "math"
)

type Point struct {
    x float64
    y float64
}

func distance(p, q *Point) float64 {
    dx := p.x - q.x
    dy := p.y - q.y
    return math.Sqrt(dx * dx + dy * dy)
}

func main() {
    var o *Point = &Point{}
    var p *Point = &Point{ 10.0, 10.0 }
    var q *Point = new(Point)
    q.x, q.y = 100.0, 200.0

    fmt.Println(o)
    fmt.Println(p)
    fmt.Println(q)

    fmt.Println(o.x)
    fmt.Println(o.y)
    fmt.Println(p.x)
    fmt.Println(p.y)
    fmt.Println(q.x)
    fmt.Println(q.y)

    fmt.Println(distance(o, p))
    fmt.Println(distance(o, q))
    fmt.Println(distance(p, q))
}
^o^ > go run struct2.go
&{0 0}
&{10 10}
&{100 200}
0
0
10
10
100
200
14.142135623730951
223.60679774997897
210.23796041628637

構造体をポインタとして使うときは、初期化用の関数を使うのが通例のようだ。次の例では、newPoint 関数がそれにあたる。この関数は、ポインタのフィールドになる値を引数にとって、構造体のポインタを返す。

package main

import (
    "fmt"
    "math"
)

type Point struct {
    x float64
    y float64
}

func newPoint(x, y float64) *Point {
    p := new(Point)
    p.x, p.y = x, y
    return p
}

func distance(p, q *Point) float64 {
    dx := p.x - q.x
    dy := p.y - q.y
    return math.Sqrt(dx * dx + dy * dy)
}

func main() {
    var p *Point = newPoint(0.0, 0.0)
    var q *Point = newPoint(10.0, 10.0)

    fmt.Println(p)
    fmt.Println(q)

    fmt.Println(p.x)
    fmt.Println(p.y)
    fmt.Println(q.x)
    fmt.Println(q.y)

    fmt.Println(distance(p, q))
}
^o^ > go run struct3.go
&{0 0}
&{10 10}
0
0
10
10
14.142135623730951

最後に、構造体をスライスに格納する例を示して今日は終わりにしよう。構造体だからと言って何も特別なことはない。

package main

import "fmt"

type Point struct {
    x, y float64
}

func newPoint(x, y float64) *Point {
    p := new(Point)
    p.x, p.y = x, y
    return p
}

func main() {
    var a []Point = []Point{
        { x: 0.0, y: 0.0 },
        { 10.0, 10.0 },
        { 100.0, 100.0 },
    }
    var b []*Point = make([]*Point, 8)

    fmt.Println(a)
    fmt.Println(b)

    for i := 0; i < 8; i++ {
        b[i] = newPoint(float64(i), float64(i))
    }
    fmt.Println(b)
    for i := 0; i < 8; i++ {
        fmt.Println(b[i])
    }
}
^o^ > go run struct_slice.go
[{0 0} {10 10} {100 100}]
[       ]
[0xc042008270 0xc042008280 0xc042008290 0xc0420082a0 0xc0420082b0 0xc0420082c0 0xc0420082d0 0xc0420082e0]
&{0 0}
&{1 1}
&{2 2}
&{3 3}
&{4 4}
&{5 5}
&{6 6}
&{7 7}