日ふわ録

日常のふわっとした記録。プライベートから仕事まで幅広く記録。

GO言語初心者が入門3(プログラミング)

前提

  • インストールや設定は以前の記事に
  • golang1.6.3

goコマンド

①プログラムを実行する

go runを利用すると気軽に実行できます

$ go run main.go

②ブログラムのビルド

go buildコンパイルして実行ファイルを生成します

# hello.goのみ存在
$ ls -la
total 4408
-rw-r--r--  1 hogehoge  1796141739       78  7 23 17:16 hello.go

# hello.goをビルドします
$ go build -o hello hello.go

# 実行ファイルのhelloができました
$ ls -la
total 4408
-rwxr-xr-x  1 hogehoge  1796141739  2249680  7 23 18:34 hello
-rw-r--r--  1 hogehoge  1796141739       78  7 23 17:16 hello.go

# 実行するとちゃんとhello.goの実行と同じになります
$ ./hello
Hello World

GO言語の実行ファイルはソースファイルに比べて大きくなります。 これはGO言語はOSによって提供される標準ライブラリに依存しないという言語仕様のためです。 (OSの機能を使わずに、GO側に同様のライブラリをもっているため)

プログラムにまつわるエトセトラ

①パッケージ (package)

  • GO言語では変数や関数などのプログラムの全ての要素はパッケージに必ず属す
  • 1つのファイルは単一のパッケージにしか属せない
  • なんでpackage宣言からプログラムは始まる
package main

②インポート (import)

  • ファイル内で利用するパッケージを指定するのがimport
  • packageの後にimport宣言がくる感じ
  • 参照されない宣言はコンパイルエラーになります(きっちりチェック)
package main

import (
    "fmt"
)

③エントリーポイント

  • main関数がGOのエントリーポイント(実行開始される場所)
  • 最低限のお決まりを実装すると以下になる
package main

import (
    "fmt"
)

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

④コメント

コメントは行コメントとブロックコメントの2種類があります。 * 行コメント

// コメントです
  • ブロックコメント
/*
  コメントです
  このブロック内はコメントです
*/

ステートメントの区切り文字

GO言語のステートメントの区切り文字は;セミコロンである。 が、セミコロンを省略できる言語仕様となっている。

以下のようなケースは独特ですので注意

   // 改行無しの場合はこれでOK
    color := [3]string{"red", "blue", "yellow"}
    fmt.Println(color[0])

    // 改行入れた場合は最後の部分にカンマがないとダメな言語仕様らしい
    color2 := [3]string{
        "red",
        "blue",
        "yellow", // 最後もカンマいれないとコンパイルエラーになる
    }
    fmt.Println(color2[0])

⑥変数

GO言語の変数の概要

  1. 値型 (整数や実数などの値を格納する型)
  2. 参照型 (スライス、マップ、チャネル)
  3. ポインタ型 (C言語同様のポインタ)

変数の宣言

// 明示的に宣言
var i int
// 複数宣言とかもできる
var x, y, z int
// こんなまとめもできる
var {
    var i int,
    name string
}

変数の代入

=演算子で代入。

var i int
i = 5  // OK
i = "test"  // コンパイルエラー

暗黙的な定義 (型推論)

型指定をせずに代入する:=演算子

// int型の変数iを宣言して1を代入
i := 1

ちなみに以下みたいなものはコンパイルエラー(:=演算子は変数定義なので)

i := 1   // OK
i := 100  // コンパイルエラー(すでにiは宣言されている)

明示的か?暗黙的か?

基本型推論

GO言語の場合は、積極的に暗黙的な宣言でよさげ。 静的型付けでありつつ、煩雑な型指定を省略できるようにしている。 そのため、GOの言語仕様のメリットを教授するためにも、 可能な限り、型推論におまかせがよさげ。

パッケージ変数とローカル変数

関数内部はローカル変数 関数外はパッケージ変数となる。 パッケージ変数はプログラム全体で1つの値を参照する

基本型

いろいろあるのでチュートリアルをみた方が早い。

golang.jp

配列型

GOの配列型は宣言した要素数から縮小や拡張できない固定である

これもチュートリアルをみてほしい

golang.jp

interface 型とnil

  • interface{}が一つの型となる。
  • GOのあらゆる型と互換性がある特殊な型である
  • Cの汎用ポインタ、JavaのObject型のような存在
var x interface{}
fmt.Println(x)

上記の実行結果は<nil>となります。 これはJavaでいうnullのようなもの。

演算子

これも多いのでチュートリアル熟読

golang.jp

⑦関数

基本的な関数

こんなの

func plus(x, y, int) int {
    return x + y
}

戻り値がない関数

func plus(x, y, int) {
    fmt.Pinrtln("OK");
}

複数の戻り値を返す関数

func div(x, y, int) (int, int){
     q := x / y
     r := x % y
     return q, r
}

q, r := div(19, 7)

関数とエラー処理

GOには例外処理機構はないので、複数の戻り値を利用してエラー処理をする

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

無名関数

関数リテラルを代入した変数的な。

f := func(x, y int) int {return x + y}
fmt.Println(f(2, 3))

実行結果は5と出力される

関数を返す関数

func ResultFunc() func() {
    return func() {
        fmt.Println("function!")
    }
}

func main() {
    f := ResultFunc()
    f()
}

関数を引数にとる関数

func ResultFunc() func() {
    return func() {
        fmt.Println("function!")
    }
}

func CallFunction(f func()) {
    f()
}

func main() {
    f := ResultFunc()
    CallFunction(f)
}

無名関数をクロージャとして利用する

func later() func(string) string {
    var store string
    return func(next string) string {
        s := store
        store = next
        return s
    }
}

func main() {
    // クロージャ
    l := later()
    fmt.Println("クロージャstart")
    fmt.Println(l("Golang"))
    fmt.Println(l("is"))
    fmt.Println(l("Good"))
    fmt.Println("クロージャend")
}

↑を実行すると

クロージャstart

Golang
is
クロージャend

となります。

ちなみにクロージャが意味不明なので以下を参考に

dqn.sakusakutto.jp

クロージャを使ったジェネレーター

func integers() func() int {
    i := 0
    return func() int {
        i += 1
        return i
    }
}

func main() {
    ints := integers()
    fmt.Println("ジェネレーターstart")
    fmt.Println(ints())
    fmt.Println(ints())
    fmt.Println(ints())
    fmt.Println("ジェネレーターend")
    ints2 := integers()
    fmt.Println("ジェネレーター2start")
    fmt.Println(ints2())
    fmt.Println(ints2())
    fmt.Println(ints2())
    fmt.Println(ints2())
    fmt.Println(ints())
    fmt.Println("ジェネレーター2end")

↑の実行結果は

ジェネレーターstart
1
2
3
ジェネレーターend
ジェネレーター2start
1
2
3
4
4
ジェネレーター2end

クロージャがつかむ変数は生成するクロージャ毎に別であることがわかる

⑧定数

constで宣言すれば定数っす

const x int = 1

⑨制御文

いっぱいあるのでこれまたチュートリアル

golang.jp

いくつか補足

  • defer : 関数終了時に実行する処理を記載できる。ファイルのクローズとかで便利
  • panic : JavaでいうとこのError系。基本そんな使わないくさい
  • recover : panicから復帰する制御みたい。これもあんまり使わないかも
  • goto : 他言語同様他ラベルへ飛ばす
  • go : ゴルーチン。並行処理を起動する
func sub() {
    fmt.Println("sub start")
    defer fmt.Println("sub end")
    for i := 0; i < 10; i++ {
        fmt.Println("sub:" + strconv.Itoa(i))
    }
    ioutil.WriteFile("/tmp/sample_go.out", []byte("hello world\n"), os.ModePerm)
}

func main() {
    // ゴルーチンで関数呼び出し
    go sub()
    // ゴルーチンで無名関数でも一緒
    go func() {
        fmt.Println("sub2 start")
        defer fmt.Println("sub2 end")
        for i := 0; i < 10; i++ {
            fmt.Println("sub2:" + strconv.Itoa(i))
        }
        ioutil.WriteFile("/tmp/sample_go.out", []byte("hello world\n"), os.ModePerm)
    }()
    // mainでもおんなじように
    for i := 0; i < 10; i++ {
        fmt.Println("main:" + strconv.Itoa(i))
    }
    // subの処理が行われる前にmainが終了しないようにとりあえず1秒スリーブ
    time.Sleep(time.Second)
}

実行するとこうなる

main:0
main:1
main:2
main:3
main:4
main:5
main:6
main:7
main:8
main:9
sub start
sub:0
sub:1
sub:2
sub:3
sub2 start
sub:4
sub:5
sub2:0
sub2:1
sub:6
sub2:2
sub2:3
sub:7
sub2:4
sub2:5
sub:8
sub2:6
sub2:7
sub:9
sub2:8
sub2:9
sub2 end
sub end
main end

⑩参照型

スライス (slice) : 可変長配列

// int型のスライス
var s = []int
// makeで生成
s := make([]int, 10)
// リテラルで生成
s := []int{1, 2, 3, 4, 5}
// 要素数を調べる
len(s)
// 容量を調べる (容量は内部的に確保されている領域)
cap(s)
// 要素追加
append(s, 6, 7, 8)
// スライスのコピー
copy(s, hogehoge)

マップ (map) : 連想配列的な

// intのキーとstringを値に持つmap
var m map[int] string
// makeで生成
m := make(map[int|string)
m[1] = "US"
m[81] = "Japan"
// mapのリテラル
m := map[int]string{1: "A", 2: "B", 3: "C"}
// 要素数
len(m)
// 要素の削除
delete(m, 2)

チャネル (chanel) : ゴルーチンとゴルーチンの間でデータを受け渡す際に利用

これもチュートリアル見た方が確実

golang.jp

⑪ポインタとか構造体とかインターフェースとか。

なんだかんだここもチュートリアルみた方がまとまってるね

golang.jp