メモリの動的割り当て

new 関数は、動的にメモリを割り当ててそのアドレス、つまりポインタを返す。

var 変数名 *型 = new(型)

例を示そう。

package main

import "fmt"

func main() {
    var p *int = new(int)
    var q *float64 = new(float64)
    var a *[8]int = new([8]int)

    fmt.Println(p)
    fmt.Println(*p)
    fmt.Println(q)
    fmt.Println(*q)
    fmt.Println(a)
    fmt.Println(*a)

    *p = 100
    *q = 1.2345
    a[0] = 10
    a[7] = 80

    fmt.Println(*p)
    fmt.Println(*q)
    fmt.Println(*a)
}
^o^ > go run dynamic_alloc.go
0xc042008210
0
0xc042008218
0
&[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
100
1.2345
[10 0 0 0 0 0 0 80]

本題と関係ないけど、配列(と、たぶんスライスも)を指すポインタから要素へのアクセスは * をつけなくてできるんだな。

ポインタ

Go のポインタは C のポインタに似ている。変数のアドレスを得るためには & を使い、ポインタのさす値を参照するには * を使う。宣言するのに * を使うのも一緒だけど、Go の場合には変数名ではなく型の前につける。

package main

import "fmt"

func main() {
    var n int = 10
    var p *int = &n

    fmt.Println(n)
    fmt.Println(*p)

    *p = 100

    fmt.Println(n)
    fmt.Println(*p)

    fmt.Println(p)

    fmt.Println(p == &n)
}
^o^ > go run pointer.go
10
10
100
100
0xc042008210
true

C と違う点はほかにもある。Go のポインタは、整数値の代入や加減算はできないようになっている。配列のアドレスもそうだ。C では配列へのポインタはその配列の先頭要素へのポインタになっていて、各要素にアクセスするにはポインタをインクリメントしたりとかする。Go ではこういうことはできない。配列へのポインタは配列そのものへのポインタであって、配列の要素へのアクセスはできないようだ。もちろん、配列の要素へのポインタは作れる。

package main

import "fmt"

func main() {
    var a [4]int = [4]int{ 1,2,3,4 }
    var b [4]int = [4]int{ 10,20,30,40 }
    var p *[4]int = &a
    p0, p1 := &a[0], &a[1]

    fmt.Println(p)
    fmt.Println(*p)
    fmt.Println(a[0])
    // fmt.Println(*p[0]) // error

    fmt.Println(p0)
    fmt.Println(*p0)

    fmt.Println(p1)
    fmt.Println(*p1)
    fmt.Println(a[1])

    *p0 = 10
    *p1 = 20
    fmt.Println(a)

    p = &b
    fmt.Println(*p)
}
^o^ > go run pointer_array.go
&[1 2 3 4]
[1 2 3 4]
1
0xc042002700
1
0xc042002708
2
2
[10 20 3 4]
[10 20 30 40]

以前に、関数の呼び出しは値渡しだと書いた。関数のポインタを渡してやると、ポインタは仮引数にコピーされるけど、そのさしている値は呼び出し元と同じ値だ。つまり、ポインタを使えば参照渡しと同等のことかできる。

package main

import "fmt"

func swap(x *int, y *int) {
    tmp := *x
    *x = *y
    *y = tmp
}

func timesArray(n int, ary *[4]int) {
    for i := 0; i < len(*ary); i++ {
        ary[i] *= n
    }
}

func main() {
    var a int = 10
    var b int = 20
    var c [4]int = [4]int{ 1,2,3,4 }

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

    swap(&a, &b)
    timesArray(10, &c)

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}
^o^ > go run pointer_call.go
10
20
[1 2 3 4]
20
10
[10 20 30 40]

さて、ポインタの基本はこんなところ。

クロージャ

昨日、一昨日のエントリで、関数を引数にとる高階関数を見てきた。今度は関数を返す関数を考えよう。
関数を返すには、return で関数を返してやればいいだけだ。それだけだと芸がないので、もう一ひねりしてみる。Go では、関数を定義した環境の変数を保持することができる。クロージャだ。
例を示そう。

package main

import "fmt"

func genCounter(init int) func() int {
    i := init
    return func() int {
        i += 1
        return i
    }
}

func main() {
    count := genCounter(0)

    fmt.Println(count())
    fmt.Println(count())
    fmt.Println(count())
}

genCounter の中で定義している匿名関数(これが genCounter の返り値になる)は、その外側にある変数 i を覚えている。なので、返り値の関数は呼び出されるごとに i をインクリメントしてから値を返す。

^o^ > go run closure.go
1
2
3

この通り。

mapcarとfilter

高階関数の練習に、mapcarfilter を作ってみた。

まずは mapcar から。

package main

import "fmt"

func mapcar(f func(int) int, a []int) []int {
    b := make([]int, 0)
    for _, x := range a {
        b = append(b, f(x))
    }
    return b
}

func square(x int) int {
    return x * x
}

func main() {
    a := []int{ 1,2,3,4,5 }
    fmt.Println(a)

    b := mapcar(square, a)
    fmt.Println(b)

    cube := func(x int) int { return x * x * x }
    c := mapcar(cube, a)
    fmt.Println(c)
}
^o^ > go run mapcar.go
[1 2 3 4 5]
[1 4 9 16 25]
[1 8 27 64 125]

つぎは filter

package main

import "fmt"

func filter(f func(int) bool, a []int) []int {
    b := make([]int, 0)
    for _, x := range a {
        if f(x) {
            b = append(b, x)
        }
    }
    return b
}

func even(i int) bool {
    return i % 2 == 0
}

func main() {
    a := []int{ 1,2,3,4,5,6,7,8,9 }
    b := filter(even, a)
    fmt.Println(b)
}
^o^ > go run filter.go
[2 4 6 8]

高階関数と匿名関数

Go では高階関数もサポートされている。
引数の型のところに関数の型を書けばいい。関数の型は func(引数の型のリスト) 返り値の型 というふうに書く。説明するより、例を見たほうが早いだろう。

package main

import "fmt"

func square(x int) int {
    return x * x
}

func cube(x int) int {
    return x * x * x
}

func sumOf(f func(int) int, m, n int) int {
    a := 0
    for ; m <= n; m++ {
        a += f(m)
    }
    return a
}

func main() {
    fmt.Println("SUm of squares:", sumOf(square, 1, 5))
    fmt.Println("SUm of cubes: ", sumOf(cube, 1, 5))
}

sumOf が高階関数。最初の引数が関数になっていて、f func(int) int と書いてあるのがそれだ。
実行すると:

^o^ > go run sumof.go
SUm of squares: 55
SUm of cubes:   225

あたりまえだけど、きちんと動く。

さて、上の例にある squarecube みたいな短い関数ならわざわざ定義してから使わなくても、使う場所、つまり sumOf の引数のところに書いてしまうこともできる。匿名関数、または無名関数ってやつだね。こんどはこの匿名関数を使ってみよう。

package main

import "fmt"

func sumOf(f func(int) int, m, n int) int {
    a := 0
    for ; m <= n; m++ {
        a += f(m)
    }
    return a
}

func main() {
    fmt.Println("Sum of squares:", sumOf(func(x int) int { return x * x }, 1, 5))
    fmt.Println("SUm of cubes: ", sumOf(func(x int) int { return x * x * x }, 1, 5))
}
^o^ > go run sumof2.go
Sum of squares: 55
SUm of cubes:   225

この通り、匿名関数をつかっても期待通りに動く。とはいえ、匿名関数も型を明示的に書かなきゃいけないのはちょっとお手軽感がないなぁ。

再帰

Go の関数は再帰呼び出しをサポートしている。よくある階乗を求めるプログラムを見てみよう。

package main

import "fmt"

func fact(n int) int {
    if n == 0 {
        return 1
    } else {
        return n * fact(n - 1)
    }
}

func main() {
    for i := 0; i < 13; i++ {
        fmt.Println(i, ":", fact(i))
    }
}
^o^ > go run fact.go
0 : 1
1 : 1
2 : 2
3 : 6
4 : 24
5 : 120
6 : 720
7 : 5040
8 : 40320
9 : 362880
10 : 3628800
11 : 39916800
12 : 479001600

func で定義した関数でなく匿名関数でも、先に関数を代入する変数を宣言しておけば、再帰呼び出しができる。

package main

import "fmt"

func main() {
    var fact func(int) int
    fact = func(n int) int {
        if n == 0 {
            return 1
        } else {
            return n * fact(n - 1)
        }
    }

    for i := 0; i < 13; i++ {
        fmt.Println(i, ":", fact(i))
    }
}
^o^ > go run fact2.go
0 : 1
1 : 1
2 : 2
3 : 6
4 : 24
5 : 120
6 : 720
7 : 5040
8 : 40320
9 : 362880
10 : 3628800
11 : 39916800
12 : 479001600

匿名関数については、また改めて書く。

スライスのコピー

copy 関数を使う。コピー元のスライスより、コピー先のほうが長い場合はもちろん、短い場合でもエラーにならない。長さはコピー先のスライスに合わせられる。

package main

import "fmt"

func main() {
    a := []int{ 1,2,3,4,5,6,7,8 }
    b := []int{ 10,20,30,40,50,60,70,80,90,100 }
    c := []int{ 10,20,30,40 }
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)

    copy(b, a)
    copy(c, a)
    fmt.Println(b)
    fmt.Println(c)
}
^o^ > go run slice_copy.go
[1 2 3 4 5 6 7 8]
[10 20 30 40 50 60 70 80 90 100]
[10 20 30 40]
[1 2 3 4 5 6 7 8 90 100]
[1 2 3 4]

値渡しと参照渡し

関数への引数の渡し方には、値渡しと参照渡しがある。Go は基本的に値渡しだ。つまり、実引数の値が関数に渡されるときに仮引数にコピーされる。例を挙げよう。

package main

import "fmt"

func foo(x int, y string) (int, string) {
    x = 10
    y += "def"
    fmt.Println(x, y)
    return x, y
}

func main() {
    x := 1
    y := "abc"
    fmt.Println(x, y)

    foo(x, y)
    fmt.Println(x, y)
}
^o^ > go run funccall.go
1 abc
10 abcdef
1 abc

関数 foo の中で x は再代入されているし、y は破壊的に変更されているけど、それでも関数から出た後の出力を見ると、値が元に戻っている。main の中の xyfoo の中の xy が別のものだってことだ。

ところが、よくわからないんだけど、スライスやマップ(配列も?)の場合には、呼び出した関数の中で破壊的に変更すると、呼び出し元でも変更されている。これは参照渡しのように見える。

package main

import "fmt"

func foo(x []int) []int {
    x[0] *= 10
    fmt.Println(x)
    return x
}

func main() {
    x := []int{ 1,2,3 }
    fmt.Println(x)

    foo(x)
    fmt.Println(x)
}
^o^ > go run funccall2.go
[1 2 3]
[10 2 3]
[10 2 3]

これはスライスの変数が、スライス自体ではなくスライスへの参照を保持していて、それが仮引数へコピーされるから、らしい。ともかく、これは気を付けよう。

ちなみに、ポインタを使えば参照渡しと同等のことができる。ポインタについてはそのうち書く。

可変長引数

Go の関数は可変長引数をサポートしている。可変長引数は引数リストの最後にだけ置くとこができ、args ...int のように書く。こうすると、args には引数がスライスとして格納される。

package main

import "fmt"

func foo0(args ...int) {
    fmt.Println(args)
}

func foo1(a int, args ...int) {
    fmt.Println(a, args)
}

func main() {
    foo0()
    foo0(1)
    foo0(1, 2)
    foo0(1, 2, 3)
    foo1(1)
    foo1(1, 2)
    foo1(1, 2, 3)
    foo1(1, 2, 3, 4)
}

foo0 は1つの可変長引数、言い換えると 0 個以上の引数をとり、foo1 は1つ以上の引数をとる。

^o^ > go run variable_length.go
[]
[1]
[1 2]
[1 2 3]
1 []
1 [2]
1 [2 3]
1 [2 3 4]

スライスに要素を追加する関数 append も、実は可変長引数だ。このとおり。

package main

import "fmt"

func main() {
    a := []int{ 1,2,3 }
    fmt.Println(a)

    a = append(a, 4, 5, 6)
    fmt.Println(a)
}
^o^ > go run append.go
[1 2 3]
[1 2 3 4 5 6]

可変長引数に渡すデータがスライスの時、s... のように書くと、スライスを展開して可変長引数に渡すことができる。これを利用して、スライスの連結ができる。

package main

import "fmt"

func main() {
    a := []int{ 1,2,3 }
    b := []int{ 4,5,6 }

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

    c := append(a, b...)
    fmt.Println(c)
}
^o^ > go run append2.go
[1 2 3]
[4 5 6]
[1 2 3 4 5 6]

多値と多重代入

すでにさらっと書いたけど、Go の関数は複数の値を返すことができる。いわゆる「多値」ってやつ。で、それを受け取るほうも普通に多重代入ができる。例を示すほうが早いだろう。

package main

import "fmt"

func divMod(x, y int) (int, int) {
    return x / y, x % y
}

func main() {
    p, q := divMod(10, 3)
    fmt.Println(p)
    fmt.Println(q)
}
^o^ > go run multivalue.go
3
1

見てわかるように、多値を返すには関数の返り値の型をカッコでくくって並べて書き、return に同じ数の値(もちろん型があってなきゃいけない)を渡してやるだけだ。Scheme の多値よりも扱いが簡単だね。

さて、多重代入ができるってことはこんなこともできる。

package main

import "fmt"

func main() {
    a, b := 10, 20
    fmt.Println(a, b)

    b, a = a, b
    fmt.Println(a, b)
}
^o^ > go run swap.go
10 20
20 10