サカナ未遂

プログラミング、筋トレ、子育て

Go言語を基礎を学ぶ。スターティングGo言語 3章まで読む

Go言語の勉強で書籍を探していて「スターティングGo言語」が良さそうだったので買ってみた。

スターティングGo言語 (CodeZine BOOKS)

スターティングGo言語 (CodeZine BOOKS)

  • 作者:松尾 愛賀
  • 発売日: 2016/04/15
  • メディア: 単行本(ソフトカバー)

少々前の書籍なので環境構築は、最新の情報を参照した。 Chapter2以降で学んだことを書いていく

Chapter2 プログラムの構成と実行

とりあえずいつものコードを書く hello.go

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

Goはコンパイル言語であるが、ビルドプロセスを隠蔽してプログラムを直接事項できるrunコマンドがある

$ go run hello.go
Hello, World!

エントリーポイントはmainパッケージのmain関数になる。 ビルドはbuildコマンドをつかう。ビルドすると実行ファイルが生成される.

$ go build -o hello
$ ./hello
Hello, World!

ソースコードよりビルドされたファイルのほうがサイズが大きくなる。 これはOSの標準ライブラリに依存しないため、パッケージの機能すべてを実行ファイルの中に組み込むためである。

パッケージ

1つのパッケージを複数のファイルに分けて定義することができる。一つにまとめても問題ない。

本ではanimalsパッケージを作成し、mainで読み込んで実行とあるが動かなかった。

package main

import (
    "fmt"

    "./animals"
)

func main() {
.
.

ググったところ、go.modで初期化した場所がルートになるのでそこからのパスを設定しないと動かないっぽい

go mod init terachan.com/sample
package main

import (
    "fmt"

    "terachan.com/sample/animals" // ← こんな感じにする
)

こうすることで動いた。

パッケージは1つのディレクトリには1つのパッケージしか定義できない 同じパッケージの関数(同一フォルダ)は、main.goでimportしなくても使用できる。

テストは標準パッケージでテスト機能testingがついてる。 XXXX_test.goのファイルをつくってテストコードを書く。

package hogehgoe

import "testing"

func TestTargetApp(t *testing.T) {
    expect := "success"
    actual := TargetApp()

    if expect != actual {
        t.Errorf("%s != %s", expect, actual)
    }
}

テスト実行

$ go test -v ./hogehoge/
=== RUN   TestElephantFeed
--- PASS: TestElephantFeed (0.00s)
PASS
ok      terachan.com/sample/hogehoge     0.063s

Chapter3 言語の基本

fmt.Printfでデバッグをするときは%v、%#v、%Tが便利。さまざまな型を見やすい形式で埋め込んでくれる。

  • %v:さまざまな型のデータを埋め込む
  • %#v:Goのリテラル表現でデータを埋め込む
  • %T:データの型情報を埋め込む

明示的に書けるが、:=を使うと代入時に型推論してくれる。

// 明示的に宣言
var n int
n = 10

// 型推論
i := 10

// 型推論。これもいける。複数を定義するときはこれのほうが読みやすい
var {
    n = 1
    s = "hogehoge"
    b = false
}

変数

ローカル変数とパッケージ変数に分かれる。 main関数の外側で定義すればパッケージ変数となり、どの関数からでも呼び出せる。

参照されてない変数がある場合はコンパイルエラーになる。 これは地味に便利で、何に使ってるかわからない変数問題がなくなる。 float32は原則使用しないほうがよい。64のほうが精度が高い。メモリの使用効率野天では32のほうが省メモリではあるが。

配列

配列の定義

a := [5]int{1, 2, 3, 4, 5}

要素のサイズが5のint型の配列の定義となる。 値が4つしかない場合は、5つめの要素は0になる。 要素数を超過した場合はエラーになる。 配列はGoで定義されたすべての型で配列型を定義できる。

素数の省略

a := [...]int{1, 2, 3, 4, 5}

[...]を使うことで初期値の数でサイズが決まる。

配列の要素の型が同じでも、要素数が異なると変数同士の代入はエラーになる

var {
    hoge1 [5]int
    hoge2 [6]int
}
// これはエラーになる
hoge1 = hoge2

素数が同じでも型が異なればもちろんエラーになる。

interface{}

interface{}型は{}も含めて型の名前。 Goのあらゆる型と互換性のある特殊な型

var x interface{}
// なんでも入る
x = "hogehgoe"

関数

Goはオブジェクト指向機能を持っていないので「関数の定義」と「構造体の定義」をすることがプログラミングにおける中心的な作業になる。 関数に戻り値がないvoid型はない(CやJavatとは違う) returnで複数の値を返すことができる。(Rubyみたい) 関数の戻り値を受け取るときに_をすると戻り値を破棄することができる

q, _ := myMethod()

// 2つとも破棄はできない
_, _ := myMethod()

エラー処理の書き方は以下のように書き、よく出てくる表現

result, err := myMethod()
if (err != nil) {
    // エラー処理
    ...
}

一見引数を返さないように見える関数

func myMethod() (a int) {
    a = 10
    return
}

この場合、関数名のところに(a int)とあるが、これが戻り地として暗黙的に定義されている。 明示的にreturn aとしなくてもreturnと書くだけでaの値を返してくれるので、この場合、myMethodを呼び出すと10が帰ってくる。

関数の引数の無視

func ignoreArgs(_, _ int) int {
    return 1
}

引数を無視することに意味はなさそうだけど、特定のインターフェースに属する場合、定義上引数が必要になるが実装上不要な場合がある。 そのようなときに引数を無視する実装にする必要がある。

無名関数

関数を値として表現したもの。関数を関数の引数にすることも、戻り地にすることもできる。

f := func(x, y int) int { return x + y }

この場合、int型の引数を2つもらってint型の値を返す型になる。

クロージャ

クロージャは関数と関数の処理に関係する「関数外」の環境をセットにして閉じ込めたもの。 自分が作られた環境の外の変数への参照を保持した関数である。 クロージャを生成し直すと関数外の変数が新しく生成される。

スコープ

1文字目が大文字であれば他のパッケージから参照可能 publicとprivateみたいなものか。 別パッケージの小文字で定義されたものを呼び出すとコンパイルエラーとなる。

ファイルのスコープ

1つのパッケージで複数のファイルを定義することができるが、importするパッケージは、別ファイルのものを参照することはできない。

簡易文付きif

if [簡易文]; [条件式] {

}

エラー処理の場合とかに有効。

if _., err := doSometiong(); err != nil
{
    // エラー処理
}

関数を実行して、エラーかどうか判定し、エラーだったらエラー処理を実行する例。

goのインデントについて

fmtを適用するとインデントがすべてタブ文字になる。これによってインデントは2から4かで争う必要がない。 標準でリンターがあるのが嬉しいし悩まなくてすむ。JavascriptRubyはあとからリンターいれるのきつい。

for文

配列に対するループの書き方

member := [3]string{"Alice", "Bob", "Ken"}

for i, s := range member {
    fmt.Printf("member[%d] = %s\n", i, s)
}

range memberの戻り値iにインデックス、sに配列の要素が入り、配列の最後までループする。

switch文

明示的にfallthroughを書かない限り、case節の実行がおわるとswitch文が終了する。 fallthroughをつけると、次の条件も判定するので、defaultがある場合は必ず通る。 case節に式を書くこともできる。

型によるswitch文

interface型に適当な値を入れた場合、switchで.(type)を使うことで型を判定できる。

var str interface{}
str =  1

switch str.(type) {
case string:
    fmt.Println("文字列型")
default:
    fmt.Println("ヒットなし")
}

defer

関数の終了時に実行される式を登録することができる。 deferが複数ある場合、あとから定義されたほうから先に実行される。後入れ先出しなのね。 どういう場合につかうかっていうと、ファイルオープン処理時にdeferでクローズ書いておくなどに使う。

panicとrecover

panicを実行するとランタイムパニックが発生して実行中の関数は中断される。 recoverを使うとパニックで中断したプログラムを回復させることができる。panicはrecoverと組み合わせて使うのが原則

go文

並列処理、go methodnameで使う。 簡単に並列処理を書くことができる。

init

初期化する。 複数かくことができ、その場合ソースコードに出現した順序で実行される。

所感

以前簡単にGoを勉強したがまったく忘れていたので良い復習になった。 引き続きよんでいく。