はじめに
Go の勉強のため、A Tour of Goに取り組んでいる
今回はPackages, variables, and functions
章について、学んだことを記していく
まとめ記事はこちら
使えそうなスニペット
※筆者は VSCode を使用
- スニペットは最初の一文字を入力するだけで予測変換に出てくるので、全部を入力する必要はない
- また、文字を飛ばしても良い
- 以下の例でいうと、
pm
と入力してもpkgm
が予測変換に出る
pkgm
-> package main
fm
-> func main(){ }
func
-> func (){ }
fp
-> fmt.Println("")
ff
-> fmt.Printf("", var)
iferr
-> if err != nil { return nil, err}
ページごとの補足
A Tour of Go
を進めていく中で、疑問に思ったことをまとめた
Packages
Import する package 名は、Import Path の最後の要素になる
- Ex:
math/rand
->rand
同じ名前のパッケージをインポートしたい場合は、Package Name を指定する
また、Package Name にドット(.
)を指定すると、修飾子なしにメソッドにアクセスできる
// Import declaration Local name of Sin
import "lib/math" // math.Sin
import m "lib/math" // m.Sin
import . "lib/math" // Sin
Ref. The Go Programming Language Specification
math/rand
の使い方
math/rand
はシード値が同じであれば同じ疑似乱数を返すため、実行するたびに値を変更したければSeed
メソッドを都度呼ぶ必要がある
また、Seed
メソッドの引数にtime.Now().UnixNano()
を使うことで、疑似乱数が作りやすくなる。
import (
"fmt"
"math/rand"
"time"
)
func main() {
fmt.Println(rand.Intn(10)) // 1 固定
rand.Seed(10)
fmt.Println(rand.Intn(10)) // 4 固定
rand.Seed(time.Now().UnixNano())
fmt.Println(rand.Intn(10)) // 毎回変わる
}
ただ、より安全性の高い乱数を作りたい場合はcrypto/rand
を使用する
package main
import (
"crypto/rand"
"fmt"
"math/big"
)
func main() {
n, err := rand.Int(rand.Reader, big.NewInt(10))
if err != nil {
fmt.Println(err)
}
fmt.Println(n)
}
Ref. Go の math/rand と crypto/rand
Imports
複数import
する際は Factored Import Statement で記述する
(Factored は直訳で因数分解、意訳でグループ化らしい)
import
に限らず、var
, const
で複数定義する際も、これを用いる
ちなみに、1 つだけ定義した場合もこのスタイルで書ける模様
(フォーマッタに添削されないということは、割とスタンダードなのだろうか)
import (
"fmt"
"math"
)
func main() {
const (
x = 1
y = 2
)
var (
z = math.Pi
)
fmt.Println(z)
}
Exported names
大文字の名前はエクスポートされ、小文字はされない
package foo
func Test() string {
return "Success"
}
func test() string {
return "Failure"
}
const (
CONSTANT = 1
constant = 2
)
package main
import (
"./foo"
"fmt"
)
func main() {
fmt.Println(foo.Test())
fmt.Println(foo.CONSTANT)
// Error: cannot refer to unexported name foo.test
// Error: cannot refer to unexported name foo.constant
fmt.Println(foo.test())
fmt.Println(foo.constant)
}
ちなみに、ファイル分割の方法は以下が参考になった
Ref. Go 言語の package の作り方: 長くなったコードを別ファイルに切り出す方法
Functions
C 言語や Java 等とは異なり、変数名 型名
の順に記述する
理由はこちら
※和訳は、こちらがわかりやすかった
一言で言えば、「こっちのほうが可読性が良いから」
Multiple results
複数の戻り値を返すことができる
戻り値の一部を使わない場合は、受け取る変数名を_
にすれば良い
こうしないとコンパイル時にdeclared but not used
とエラーが出る
package main
import (
"fmt"
)
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, _ := swap("Hello", "World")
fmt.Println(a)
}
Basic Types
rune(ルーン) とは
'
で定義される文字列であり、Unicode のコードポイントを指す。中身は int32 の alias である
コードポイントについてはこちらが分かりやすい
rune と string の違い
※以下の記事を参考にした
実は、string 型は、値を Byte 配列で格納している
そのため、単純に for 文で回すと以下のようになる
x := "aあ"
for i := 0; i < len(x); i++ {
fmt.Printf("% x", x[i])
}
// 61 e3 81 82
for i := 0; i < len(x); i++ {
fmt.Printf("% v", x[i])
}
// 97 227 129 130
1文字を1文字として扱うには、for range構文を使う必要がある
for range 構文で string を回すと、rune が取れる
x := "aあ"
for _, y := range x {
fmt.Printf("% x", y)
}
// 61 3042
fmt.Println("")
for _, y := range x {
fmt.Printf(" %c", y)
}
// a あ
※%c
は整数(コードポイント)に対応する文字を返す
Ref. fmt.Printf なんかこわくない
Zero Values
変数に初期値が与えられないとゼロ値が与えられる
逆に、変数にゼロを与えた場合は、表記によって型が変わる
fmt.Printf("%T", 0) // int
fmt.Printf("%T", 0.0) // float64
蛇足
ルーンって型名を使ってるだけで、Go 言語が大好きになった
以下を忘れないようにしよう
(キャスニキの呪文のコードポイントも調べようとしたけど、長すぎるので断念……)
ansz := 'ᚨ' // アンサズ: 発火のルーン
fmt.Printf("%d, %x", ansz, ansz) // 5800, 16a8