Go言語:関数(クロージャ)
クロージャな話
試してみた
スコープは他の言語のそれと同じ。
** 1 1 1 ** 1 1 1 ** 2 1 2 ** 2 2 4 ** 3 1 3 ** 3 3 27 ** 1 1 1 ** 2 4 16 ** 3 27 7.625597484987e+12 ** 1 1 1 ** 1 1 1 ** 2 1 2 ** 2 2 4 ** 3 4 81 ** 3 81 4.434264882430377e+38 ** 1 4.434264882430377e+38 1 ** 2 1 2 ** 3 2 9
試してみた:演習
この演習に対しての回答。
0 0 1 1 2 1 3 2 4 3 5 5 6 8 7 13 8 21 9 34
フィボナッチだけど、呼び出しごとに値を返せば良い、呼び出しに対してインデックスは与えられない、ということで、できるだけ簡単な実装にしてみている。
switchはまだ出てきてないけど、、、他言語と同じで、breakを書かなくてよい、という文法の理解のみで使用。
数列の0番目と1番目が固定値になるのは、フィボナッチ数 - Wikipediaから定義を拝借。
Go言語:Map
Mapの話
- mapの型の指定は、キーと値の型をそれぞれ指定する。
map[キーの型]値の型
という形式。map[string]int
みたいな。
- 作成するには、
make
関数、もしくは初期化子を使う。- make関数、空の初期化子で作ったmapは、空。
- 初期化前のmapは、nil
- nilのmapに要素を割り当てられない。
panic: assignment to entry in nil map
になる。
- 初期化時には、必ずキーが必要。
- structのフィールドを明示する方法に同じ。
- 初期化したmapの値は、指定された型のzero valueが与えられる。
- ブラケットとキー
[キー]
でアクセス可能。- キーと値を削除するには、
delete
関数を使用する。
- キーと値を削除するには、
- ブラケットでキーにアクセスした返り値の2つ目は、キーが存在するかどうかの
bool
値を返す。- キーが存在しない場合に
false
。
- キーが存在しない場合に
試してみた1
withNilMap map[] 0 true withMakeMap map[] 0 withEmptyInitMap map[] 0 withInitMap map[fuga:2 hoge:1] 1 1 true 0 false
今更ながら気づいた(正確じゃないかも。要確認)
- 初期化子を使って初期化するときに、複数行で値を渡す場合、最後の要素の後にもカンマが必要。
- ないと文法エラーになる。
syntax error: need trailing comma before newline in composite literal
- ないと文法エラーになる。
- 複数の値を返り値とする場合、受け取り側の実装による挙動が、Rubyと少し違う?
- 違うっぽい。
- 返り値を返さない関数を、返り値を期待した使い方をすると
関数() used as value
エラーになる。 - 変数の数が返り値の数にあっていないと、
assignment count mismatch: 2 = 1
みたいなエラーがでる。 fmt.Println
はうまく受け取ってくれる。
- Mapの値に対するアクセスの戻り値は、以下の様な挙動っぽい。
- 0個の返り値を関数の引数にする場合:1つ目の返り値が関数の引数になる。(
fmt.Println
の挙動から) - 1個の返り値を受け取る場合:1つ目の返り値のみ受け取る。
- 0個の返り値を関数の引数にする場合:1つ目の返り値が関数の引数になる。(
コメントアウトを外すと、いい感じにエラーする。
試してみた2
演習のコード。
例えばrubyのハッシュの用に、新しいキーに対するカウントをインクリメントするときにも、zero valueで初期化されているので、キーの有無にかかわらずにインクリメントできる。
PASS f("I am learning Go!") = map[string]int{"learning":1, "Go!":1, "I":1, "am":1} PASS f("The quick brown fox jumped over the lazy dog.") = map[string]int{"brown":1, "over":1, "lazy":1, "dog.":1, "the":1, "The":1, "quick":1, "fox":1, "jumped":1} PASS f("I ate a donut. Then I ate another donut.") = map[string]int{"I":2, "ate":2, "a":1, "donut.":2, "Then":1, "another":1} PASS f("A man a plan a canal panama.") = map[string]int{"panama.":1, "A":1, "man":1, "a":2, "plan":1, "canal":1}
Go言語:スライス、ループの演習から
演習の内容から(メモ的な)
Go言語で面白いと思うこと
リモートサーバーのライブラリを簡単にインポートできる。
import "code.google.com/p/go-tour/pic"
関数そのものを引数に渡すことができる。
func main() {
pic.Show(Pic)
}
渡された側の実装がこちら
メモな演習の実装
func Pic(dx, dy int) [][]uint8 { x := make( [][]uint8, dx ) for i := range x { y := make( []uint8, dy ) for j := range y { y[ j ] = uint8( rand.Int() ) } x[ i ] = y } return x }
Go言語:for-rangeループ
for-range
ループは、Slice(とかMap)の要素を簡単にループ処理する。- Go1.4から、インデックス/値を変数に代入する文法を省略可能。
- Sliceの要素数分だけのループ処理を簡単にかける。
- インデックスの値を使わずに値だけを使う場合、アンダースコア
_
に代入して、破棄する。- アンダースコア
_
を変数としては使えない。
- アンダースコア
試してみた
rangeWith2Var 0 10 1 20 2 30 3 40 4 50 5 60 6 70 7 80 8 90 rangeWith1VarForIndex 0 1 2 3 4 5 6 7 8 rangeWith1VarForValue 10 20 30 40 50 60 70 80 90 rangeWithoutVar *********
コードの中のコメントを解いて、アンダースコア_
を変数として使ってみるとエラーする。
./sample.go:31: cannot use _ as value
Go言語:スライス
スライスの話(go-tourは、なぜかArrayをとばしてSliceの話をする)
SliceとArrayは異なる。こちらを参照してお勉強。
あとはこちらのSlideShareを参照する。
Array
- Go言語のArrayは長さと要素の型を明らかにしたもの
- Arrayの初期化は構造体とだいたい同じ。
Slice
- Arrayの抽象的なもの(らしい)(抽象的とは?)
- Go言語ではArrayはあまり使われずに、Sliceが使われる。
- Sliceは、Arrayへのポインターと長さと容量で構成される。
- Sliceの初期化は、Arrayのそれとだいたい同じ。
[]型{ 初期化する値 }
- 要素数がいらない。
...
も書かない。
- 初期化時に値が渡されない場合、nilになる。
- 初期化の文法を使わずに、
make
関数を使うことでも作成可能。make([]T, len, cap)
cap
を省略した場合は、len == cap
になる。cap
がlen
よりも短い場合、エラーする。len
で指定された長さ分(0からlen
個分)の要素がzero valueで初期化される。
len
以内の要素の操作slice[2]
、slice[1] = 2
のように操作可能。- 代入は(当然ながら)破壊的。
len
を超える要素(というかインデックス)の操作slice[5]
のような操作をするとpanic。- 要素を追加するには、
append
を使う。 append( slice, T... ) []T
append
は破壊的ではない。- `容量を超える量を追加しようとすると、自動的に容量を追加してくれる。
- 再スライス可能。
- 部分的に取り出して、別のスライスにする。
- 配列部分はポインターなので、別のスライスになっても、同じメモリ領域をみている。
- スライスのインデックスの取り出し方。
slice[ : ]
-> すべての要素のスライス。slice[ lo:hi ] ->
lo番目から
hi - 1`番目の要素のスライス。slice[ lo: ]
->lo
番目以降の要素のスライス。slice[ :hi ]
->hi - 1
番目までの要素のスライス。
- 要素を削除する関数は用意されていない。
Array
以下の様な感じ。
func main() { ary1 := [3]string{ "dog", "cat" } fmt.Println( ary1 ) fmt.Println( ary1[0] ) fmt.Println( ary1[1] ) fmt.Println( ary1[2] ) ary2 := [3]int{ 10, 20 } fmt.Println( ary2 ) fmt.Println( ary2[0] ) fmt.Println( ary2[1] ) fmt.Println( ary2[2] ) // fmt.Println( ary2[3] ) } //=> // [dog cat ] // dog // cat // // [10 20 0] // 10 // 20 // 0
コメントアウトしている行を解くと、
invalid array index 3 (out of bounds for 3-element array) [process exited with non-zero status]
slice
基本的な操作を試してみる。
func main() { var sl0 []int fmt.Println( sl0 ) fmt.Println( len( sl0 ) ) fmt.Println( cap( sl0 ) ) sl1 := make([]int, 5) fmt.Println( sl1 ) fmt.Println( len( sl1 ) ) fmt.Println( cap( sl1 ) ) sl2 := make([]int, 5, 8 ) fmt.Println( sl2 ) fmt.Println( len( sl2 ) ) fmt.Println( cap( sl2 ) ) sl2[4] = 1 fmt.Println( sl2 ) fmt.Println( sl2[4] ) // fmt.Println( sl2[5] ) sl3 := append( sl2, 3, 4, 5, 6 ) fmt.Println( sl2 ) fmt.Println( len( sl2 ) ) fmt.Println( cap( sl2 ) ) fmt.Println( sl3 ) fmt.Println( len( sl3 ) ) fmt.Println( cap( sl3 ) ) } //=> // [] // 0 // 0 // [0 0 0 0 0] // 5 // 5 // [0 0 0 0 0] // 5 // 8 // [0 0 0 0 1] // 1 // [0 0 0 0 1] // 5 // 8 // [0 0 0 0 1 3 4 5 6] // 9 // 16
コメントアウトしている行を解くと、
panic: runtime error: index out of range [process exited with non-zero status]
slicing slices
[10 20 30 40 50 60 70 80 90] [40 50 60 70] [60 70 80 90] [40 50 60] [10 20 30 40 51 60 70 80 90] [40 51 60 70] [60 70 80 90] [40 51 60] [10 20 30 40 52 60 70 80 90] [40 52 60 70] [60 70 80 90] [40 52 60]
Go言語:構造体とポインタ
Go言語:構造体 - sugilogのブログ のエントリーで、ポインタの部分の理解が曖昧だったので、復習。
参照したのは、こちら(M.Hiroi's Home Page / お気楽 Go 言語プログラミング入門)の記事
まずCのポインタのの利点
(1) 「参照呼び (call by reference) 」を実現する
(2) コンパクトで効率的なプログラムを書くことができる
Goでは、
- Cとはできることが異なる。
- ポインタについての整数値の代入によるアドレスの指定や演算はできない。
- ポインタを利用した利点はそのままに、危険な操作ができないようになっている。
できることをまとめる
*
のつく型は、その型のポインタ変数。- 初期値が指定されていない場合は、nilポインタに初期化される。(CのNULL相当)
&
は、アドレス演算子。(Cの&
と同様)- ポインタ変数の値を参照する場合は、変数のアクセスに
*
をつける。 - ポインタ変数でない変数に
*
をつけてアクセスすると、エラーになる。
- ポインタ変数の値を参照する場合は、変数のアクセスに
- ポインタ変数に値を代入する場合も、変数のアクセスに
*
をつける。- ポインタに対して書き換えを行うので、同じポインタを参照する変数も書き換わる。
func main() { var n int = 1 var p *int = &n fmt.Println( n ) fmt.Println( &n ) fmt.Println( p ) fmt.Println( *p ) } //=> // 1 // 0x10328000 // 0x10328000 // 1
func main() { var n int = 1 fmt.Println( *n ) } //=> // invalid indirect of n (type int) // [process exited with non-zero status]
func main() { var n int = 1 var p *int = &n fmt.Println( n ) fmt.Println( &n ) fmt.Println( p ) fmt.Println( *p ) *p = 2 fmt.Println( n ) fmt.Println( &n ) fmt.Println( p ) fmt.Println( *p ) } //=> // 1 // 0x10328000 // 0x10328000 // 1 // 2 // 0x10328000 // 0x10328000 // 2
構造体とポインタ
参照した記事では、配列で説明しているけど、今は構造体に置き換えていく。
アドレス演算子付きで構造体を初期化。(patternA)
- 出力結果で、1つ目は
&
が付いているので、ポインタであることを示している。 - 2つ目は、
*
をつけてアクセスしたので、値を示している。
ポインタを渡して、別の変数でフィールドの書き換えを行う。(patternB)
- (話の流れ的に当たり前だけど)ポインタを渡した元の変数のフィールドも、同様にして書き換えられる。
- 説明的には変数のフィールドが書き換わるというのはおかしいな。参照している先が変わったというべきか。
- だってポインタだもの。
値を渡して、別の変数でフィールドの書き換えを行う。(patternC)
- 値を渡した元の変数に変化はない。
- だって、ポインタが違うもの。
new
関数でゼロ初期化。(patternD、patternE)
- アドレス演算子をつけて初期化したパターンと同じ挙動をする。
- 値を代入するパターンも同様。
Go言語:構造体
struct
はフィールドの集まり。- フィールドへは、
.
(ドット)でアクセスする。
- フィールドへは、
type
ステートメントで定義する。- ポインタ演算はないが、ポインタを通した間接的なアクセスは可能。
- 初期化のための値の指定は、ブレース
{}
内で指定する。struct
のフィールドの定義順に、値を指定する。(部分的な無指定ができない。)struct
のフィールド名をつけて、順不同に指定する。(一部分のみのアサイン可能。)
new
関数を使うことで、0
初期化して、ポインタを返す。- 構造体のフィールドで、
0
初期化しなかった場合の値のデフォルト値。- 文字列:
""
(から文字) - 数値:
0
(ゼロ) - 真偽値:
false
- 文字列: