YOKOHEI.COM

YOKOHEI.COM

›目次

目次

  • Ch 1 チュートリアル
  • Ch 2 プログラム構造

Golang Ch 1 チュートリアル

はじめに

この本をベースに進めていく。

The Go Programming Language
https://www.gopl.io/

Source code
https://github.com/adonovan/gopl.io/

GOPL solutions
https://github.com/torbiak/gopl
https://github.com/kdama/gopl

環境構築

$ brew install go
$ go version
go version go1.12.5 darwin/amd64
$ mkdir $HOME/go
$ echo 'export GOPATH=$(go env GOPATH)' >> ~/.bash_profile
$ echo 'export PATH=$PATH:$(go env GOPATH)/bin' >> ~/.bash_profile
$ source ~/.bash_profile
$ go env GOPATH
/Users/yukihira/go
$ go env GOROOT
/usr/local/Cellar/go/1.12.5/libexec

本書の Source code 確認

$ export GOPATH=$HOME/gobook            # choose workspace directory
$ go get gopl.io/ch1/helloworld         # fetch, build, install
$ $GOPATH/bin/helloworld                # run
Hello, 世界

第 1 章 : チュートリアル

1.1 ハロー、ワールド

  • Go のコードはパッケージで構成されている
  • パッケージとは、他の言語におけるライブラリやモジュールみたいなもの
  • import 宣言で、コンパイラに対してどのパッケージが必要か伝える
  • 必要とするパッケージを正確にインポートする必要があり、過不足なく宣言する
  • 同じ行に 2 つ以上の文や宣言が現れない限り、セミコロンを必要としない
  • つまり、改行位置が問題となりうる
  • gomft ツールによりコードを標準書式に書き換えることができるし、するべき

1.2 コマンドライン引数

os パッケージはプラットフォームから独立した形式でオペレーティングシステムを扱うための関数や値を与える。
os パッケージの一部である Args という変数によりコマンドライン引数を利用する事ができる。

os.Args は文字列のスライスである。スライスとは go における基本的な概念で、簡単に言えば動的に大きさが決まる配列要素のシーケンスみたいなイメージ。

var 宣言

変数が明示的に初期化されていない場合は、暗黙的に変数の型に対する"ゼロ値"に初期化される。例えば数値型に対しては 0 で、文字列に対しては空文字列 "" となる。

for ループ

for initialization; condition; post {
    // 0 個以上の文
}

開き波括弧 ({) は post 文と同じ行でないといけない。

initialization

initialization は省略可能で、ループ開始前に実行される。また、これは単文でなくてはならない。つまり、省略変数宣言、インクリメント文、代入文、あるいは関数呼び出しとなる。

condition

condition は boolean 形式で、ループの各繰り返し前に評価される。 true 評価されればループで制御されている文が実行される。

post

post はループの本体のあとに実行され、それから condition が再び評価される。

例えば、 initializaton と post が省略されれば、セミコロンも省略してよい。

// 従来の while ループ
for condition {
    // ...
}

また、どの形式でも condition が完全に省略されると無限ループになる。

// 従来の無限ループ
for {
    // ...
}

この形式のループは break や return で終了可能。

range を繰り返す for ループ

サンプル

package main

import (
    "fmt"
    "os"
)

func main() {
    s, sep := "", ""
    for _, arg := range os.Args[1:] {
        s += sep + arg
        sep = " "
    }
    fmt.Println(s)
}

ループの各繰り返しで、 range は 2 つの値を生成する。「インデックス」と「そのインデックスの位置にある要素の値」である。インデックスが不要な場合、適当な変数に値を入れてもいいが、 Go は使わないローカル変数を許していない。
このときの解決策が _ (アンダースコア) で表されるブランク変数である。構文としては変数名が必要だけど、プログラムがその変数を必要としない場所で使える。

初期化

文字列変数の初期化は以下の 4 パターン考えられる。

s := ""
var s string
var s = ""
var s string = ""
  • 1 つめ
    簡潔だが、関数内で使うだけなら良いもののパッケージレベルの変数には使えない。
  • 2 つめ
    文字列に対するデフォルトの初期値 "" に頼った形式。
  • 3 つめ
    複数の変数を宣言するときを除いて殆ど使われない。
  • 4 つめ
    変数の型について明示的で、変数と初期値が同じ型出ないときに有効。

実際には最初の 2 つのどちらかを使うべき。

1.3 重複した行を見つける

(dup1 のサンプルコード)

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    counts := make(map[string]int)
    input := bufio.NewScanner(os.Stdin)
    for input.Scan() {
        counts[input.Text()]++
    }
    // NOTE: ignoring potential errors from input.Err()
    for line, n := range counts {
        if n > 1 {
            fmt.Printf("%d\t%s\n", n, line)
        }
    }
}

map

map は、キーと値の組の集まりを保持する。格納、取り出し、要素の検査などの機能を提供する。
キーは、その値が == で比較できる型であればなんでも取れる。よくある例は文字列。値はどのような型であっても構わない。
サンプルコード make(map[string]int) は、キーが文字列で値が int である。

bufio パッケージ

このパッケージは入出力を効率的かつ便利に扱うものである。
入力を読み込み行や単語に分解可能で、自然に行で表現される入力を容易に処理することができる。
サンプルコードでは、 bufio.Scanner を参照する新たな変数 input を定義するために省略変数宣言を使っている。
また、 input.Scan() を呼び出すごとに、次の行を読み込み、行末から改行文字を取り除く。その結果は input.Text() を呼び出すことで取り出し可能。これ以上入力がなくなると false を返す。

ファイルの読み込み

os.Open

これは 2 つの値を返す。
1 つ目は開かれたファイル (*os.File) で、 Scanner による読み込みで使う。
2 つ目は error 型の値。この値が特別な組み込み値の nil と等しければ、ファイルのオープンは成功したことになる。

これは、ストリームモードで動作する。ストリームモードでは必要に応じて入力が読み込まれて行に分解されるので、原理的には任意の量の入力を処理できる。

ReadFile (by io/ioutil)

指定されたファイルを読み込む関数。ファイルの内容全体をメモリに読み込む。
ReadFile は string に変換されるであろうバイトスライスを返すので、 strings.Split などで返り値を分割できる。

1.4 GIF アニメーション

Package gif
https://golang.org/pkg/image/gif/

Go 言語でアニメーション GIF を作成する
http://tech.nitoyon.com/ja/blog/2016/01/07/go-animated-gif-gen/

golangで画像を扱う(imageパッケージを使う)
https://www.write-ahead-log.net/entry/2017/04/23/001743

const 宣言

パッケージレベルに書いてもいいし (パッケージ全体で参照可能になる) 、関数内で書いてもいい (関数内からのみ参照可能になる) 。

コンポジットリテラル

要素の値の並びから Go のコンポジット型のインスタンスを生成するためのもの。

1.5 URL からの取得

一部抜粋

func main() {
    for _, url := range os.Args[1:] {
        resp, err := http.Get(url)
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
            os.Exit(1)
        }
        b, err := ioutil.ReadAll(resp.Body)
        resp.Body.Close()
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
            os.Exit(1)
        }
        fmt.Printf("%s", b)
    }
}

http.Get

HTTP リクエストを発行して、エラーがなければレスポンス構造体を返すもの。
レスポンスの body は読み込み可能なストリームとして結果を持っている。何がストリームでなにがストリームじゃないのか、扱いの違い、などここでは述べられていない…。

1.7 ウェブサーバ

URL のクエリの取得

エラーハンドリングを省略して肝の部分だけ書く。

handler := func(w http.ResponseWriter, r *http.Request) {
    cycles := r.URL.Query()["cycles"]
    n_cycles,_ := strconv.Atoi(cycles[0])
    lissajous(w, n_cycles)
}
http.HandleFunc("/", handler)

1.8 残りの項目

switch

Go の場合は、 C のように case は次の case へと通り抜けてかない。この動作は fallthrough で無効にできる。
また、 switch はオペランドを書かないことも可能。

func Signum(x int) int {
    switch {
        case x > 0:
            return +1
        default:
            return 0
        case x < 0:
            return -1
    }
}

みたいな。これは tagless switch と呼ばれ、 switch true と一緒。

名前付き型

type 宣言により、既存の型に名前をつけることができる。構造体型の定義はたいてい長くなるので、大抵の場合名前がつけられる。

type Point struct {
    X, Y int
}
var p Point

ポインタ

Go はポインタを明示的に扱える。 & 演算子で変数のアドレスを生成し、 * 演算子でポインタが参照している変数を取り出す。
ただ、ポインタへの演算はない。

メソッドとインターフェース

メソッドは名前付き型に関連付けられた関数。 Go はほとんどどのような名前付き型に対してもメソッドを関連付けられるという点で独特。
インターフェースはまあ、ここでは他の言語におけるインターフェースと同じ感じの説明しかなかった。

Ch 2 プログラム構造 →
▼ Codes ▼
LeetCodeGitHub
▼ Profile ▼
LinkedInFlickr
▼ Logo made with DesignEvo ▼
DesignEvo
Copyright © 2020 Kohei Yoshida