【Go】Tour of Go に沿って所感を書きながら勉強する

前回の導入編がこちらです。
さあ、How to Write Go Codeをやってみるぞ!

How to Write Go Code

見てきたけど、パッケージの作り方についてやライブラリの取得方法を学べるみたいだね!
今は、よくわからなそうだから見るのをやめるぞ!

A Tour of Go

先にこっちのA Tour of Go をすることにする。
日本語ページもあるみたいだ。

time

// source: https://go-tour-jp.appspot.com/welcome/4
package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("Welcome to the playground!")

    fmt.Println("The time is", time.Now())
}

このソースを見ると以下の3つが主に予約語なのかな。

  • package
  • import
  • func

それぞれなんとなくイメージはつくね。

fmtはフォーマットのIOを制御しているらしく、Printlnのところに%vとか書けるみたい。よくあるやつだ。
timeは時間の計測と表示機能を提供しているらしいね。これもよくあるやつだ。理解できる。 time.goというファイルを作って実行しみるとエラーが出た。

/go-playground
❯ go build
# _/Users/usermei/go-playground
./time.go:8:6: main redeclared in this block
        previous declaration at ./hello.go:5:6

これはmain()がhello.goのとダブるからbuildできなかったのかな?
time.goを消してtime.goの中身をhello.goに移植してやってみる。

/go-playground
❯ ./go-playground
Welcome to the playground!
The time is 2019-04-23 01:47:41.387461 +0900 JST m=+0.001004734

やった!動いた!
けどね、試すごとにフォルダ作る必要がありそうだ。
helloとかtimeとかちょっと手間だなぁ。

go run

buildしないで実行できるコマンドgo runがありました!
こでれ、go run hello.goみたいにファイル別で実行できるようになりました。
これ以降はrunで試していきたい。

/go-playground
❯ go run hello.go
hello, world

/go-playground
❯ go run time.go
Welcome to the playground!
The time is 2019-04-23 02:14:03.485511 +0900 JST m=+0.000271016

Packages

Goはパッケージで管理されており、最初はmainパッケージを見るんだね。

// source: https://go-tour-jp.appspot.com/basics/1
package main

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10))
}

"math/rand"はmathパッケージの中のrandパッケージという2段回掘り下げたものみたいだね。
rand.Intn(10)はrandパッケージのIntn関数を呼んでいるのかな。
擬似乱数を生成できるみたいですね。
じゃあ実行しみてるぞ!

/go-playground
❯ go run packages.go
My favorite number is 1

でた!
繰り返しても同じものが出てきたので擬似乱数となっていることが分かる(にわか)。

Imports

importは(, )で囲む書き方をfactoredインポートステートメント( factored import statement )と呼ぶみたい。
もう一つ書き方があってimport "fmt"というよくある書き方ですね。
Go言語的にはfactoredインポートステートメントを進めているみたいです。
まとまりができて見やすくなるから?

// ok
import (
    "fmt"
    "math"
)

// ng?
import "fmt"
import "math"

Exported names

GO言語では、exportする名前(exported name)は頭文字が大文字だとイメージ的にpublicで小文字から始まる文字がイメージ的にprivateのようです。分かりやすいような気がする。

// publicだから参照できる
fmt.Println(math.Pi)

// privateだから参照できない
fmt.Println(math.pi)

Functions

Go言語の関数は名前を書いた後ろに型名を書くらしいです。Javaとは違います。TypeScriptとは似ていますね。

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

func main() {
    add(42, 13)
}

なんで後ろに型を指定する書き方を実装したのかはこのページにあるみたいです。訳してQiitaに投稿している方がいらっしゅいました。ありがたや。
C言語のmain()をGo風に書いた場合を見ると、左から右にかけて読むことができるらしいです。

// source: https://blog.golang.org/gos-declaration-syntax
func main(argc int, argv *[]byte) int

Functions continued

2つ以上の引数が同じ型なら一つにまとめて書くことができるらしいですね。
どっちの書き方がいいのでしょうか?ググっても分からなかった。

func foo(x int, y int) intfunc foo(x, y int) int

Multiple results

関数の戻り値を複数にできるようですね。タプルというやつか。
これは同じ型が2つ以上並んでいても省略はできないみたいです。エラーが出ます。

// source: https://go-tour-jp.appspot.com/basics/6
package main

import "fmt"

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    a, b := swap("hello", "world")
    fmt.Println(a, b)
}

Named return values

戻り値に名前をつけることができるらしいですね。JavaScriptでObjectのプロパティを指定し戻り値とするみたいものかと思ったらちょっと違う。戻り値で指定した変数を普通に関数内で使えるみたい。初めて見たぞ。関数のドキュメントとして定義しておくみたい。分かりやすいって良いよね!
そして、returnでは何も返さないとすることでokなんだ。この名前付き戻り値で何も返さないreturnのことを"naked" returnというらしい。これは短い関数の時に使って、長い関数の時は見づらいのでやめるべきらしい。

// source: https://go-tour-jp.appspot.com/basics/7
package main

import "fmt"

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

func main() {
    fmt.Println(split(17))
}

Variables, Variables with initializers

varで変数定義らしい。同じ型なら省略できるみたい。
省略した場合の代入方法もあるんだね。

var hensu, hen, su: int

var foo, bar int = 2, 4

// これもok
var hoge, fuga = 1, true

// これはng
var hoge, fuga = 1

Short variable declarations

関数内ならvarを省略できるらしいね。関数外でもできると思ってた。

func somefunc() {
    k := "syoryaku"
}

関数外に書いて怒られてみた。

syntax error: non-declaration statement outside function body

Basic types

bool
string
int

こういうのを

基本型(組み込み型)

というらしい。
他にもたくさんあるみたい。特にruneというのは初見。

int32 の別名 Unicode のコードポイントを表す

Unicode のコードポイントを表したい時に便利なんだね。使い機会はあるか知らないけど。
int32ってことは4バイトでUnicodeを表すのか。一文字4バイト使う未来。

rune

Zero values

初期値与えないと、勝手に

ゼロ値( zero value )

というのが入るみたい。JavaScriptみたいにundefinedみたいにはならないんだね。しかも、コンパイルではエラーにならないんだ。ぬるぽが減りそう。それに、書く手間が減りそう。

Type conversions

変数 v 、型 T があった場合、 T(v) は、変数 v を T 型へ変換します。

日本語難しい。型変換らしい。Go言語の型変換は全部明示的に書くらしい。エラー減りそう。

// source: https://go-tour-jp.appspot.com/basics/13
i := 42
f := float64(i)
u := uint(f)

Type inference

型の推論ができるらしい。i := 42みたいな。これを使うとレビュアーも推論できる。果たしてどっちの推論が早いかな?

Constants

定数ってやつだ。これを待っていた。基本的にvarよりもconstを使うからありがたい。

文字(character)、文字列(string)、boolean、数値(numeric)のみで使えます。

らしい。const somefunc = () => 'teisu'みたいなJavaScriptのようにはできないんだね。

定数は := を使って宣言できません。

ok。だけど、:=を使って宣言できるようになるのはconstの方がよかったのでは?知らんけど。
varの方が使い道多いのかな。 一文字目は大文字にするみたい。関数の外部でconstを使ったら半ば強制的にパブリック?
試しに小文字にしてもエラーにならなかった。

Numeric Constants

大きいあたいも表現できるらしい。needInt(大きい数)を使ってとっても大きい値を表現できるみたい。小さい値はneedFloatみたいだね。

const (
  // 1を100ビット左シフト、つまり10進数でいくつかわからんほど大きい
  Big = 1 << 100
  // Bigを99ビット右シフト、つまり10進数で2.
  Small = Big >> 99
)

For

ついにきたぞforだ。()がない。

for i := 0; i < 10; i++ {

}

初期化ステートメント: 最初のイテレーション(繰り返し)の前に初期化が実行されます 条件式: イテレーション毎に評価されます 後処理ステートメント: イテレーション毎の最後に実行されます

へー。初期化ステートメントっていくつもかけるのかな?

for i := 0, j := 0; i < 10; i++ {
  j++
}
syntax error: unexpected :=, expecting {

だめなのか。,で区切るのが間違いなのかな。

ggって探したら微妙に違った。

for i, j := 0, 1; i < 10; i++ {
  fmt.Println(j)
}
1
1
1
1
1
1
1
1
1
1

できた。

For continued

初期化ステートメント、後処理ステートメントは書かなくても動くらしい。
whileみたいだね。

for ; sum < 1000; {

}

For is Go's "while"

こっちがGo言語のwhileらしい。

for sum < 1000 {

}

Forever

無限ループらしい。これは遊びでは使わないようにしましょう。日本の一部だと補導されたり、起訴されたりします。

for {}

参考文献