こんにちは、ナナオです。
Goを触り始めるにあたり、どこで勉強するかなどをまとめます。
インストール
インストールはmiseでやりました。
mise use -g go
どこで勉強したか(するか)
GoにはA tour of Goという公式のチュートリアルが用意されているので、そこでやりました。
ローカルからこのチュートリアルを実行したい場合は以下のコマンドを実行します。
go install golang.org/x/website/tour@latest
$GOROOT/bin/tour
印象に残ったところ
naked return
関数内の変数をそのまま返却する実装方法
ほかの言語にはない特徴的な記法ですね(実務で使うかどうかは置いといて)
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))
}
whileはない
goではfor文がwhileを兼任しています。
package main
import (
"fmt"
)
func main() {
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
}
更にrustでいうloop文もforで表現可能です。
func main() {
for {
// 無限ループ
}
}
if let的なやつ
rustでいうところのif let …的な、if文で変数宣言できるやつもあります。
package main
import (
"fmt"
)
func main() {
sum := 1
for sum < 1000 {
sum += sum
if v := sum % 2; v == 0 {
fmt.Println("even: ", sum, v)
} else {
fmt.Println("odd: ", sum, v)
}
}
fmt.Println(sum)
}
条件なしのswitch
条件なしでswitch文を書くことができます。
また、switch文にはbreakは不要です(勝手に解釈する)
ちょっとこの辺は他のプログラミング言語と混濁しちゃいそうですね。
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}
defer文
対象の関数実行を遅延評価するための構文です。GOTOと似てるかな…?
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
この場合、出力は以下のようになります。
hello
world
なんのために必要なんだ?と思っていましたが、ファイルを開いた後の片付けなどで使用するようですね。(pythonでいうwith文みたいな感じ?)
関数内に複数のdeferがあった場合、実行はスタックされるようです。
package main
import "fmt"
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
出力は以下の通りです。
counting
done
9
8
7
6
5
4
3
2
1
0
構造体
構造体のメンバを参照するには単純にポインタから.で取得できます。
(Cのアロー関数みたいな感じ?)
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
p := &v
p.X = 1e9
fmt.Println(v)
}
配列とスライス
配列は長さの決まった特定の型のリストです。
配列からスライスは:で取ることができます。
この辺の挙動もpythonと同じですね。
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
s = s[1:4]
fmt.Println(s)
s = s[:2]
fmt.Println(s)
s = s[1:]
fmt.Println(s)
}
スライスの長さはlen、元の配列の長さはcapで取ることができます。
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)
// Slice the slice to give it zero length.
s = s[:0]
printSlice(s)
// Extend its length.
s = s[:4]
printSlice(s)
// Drop its first two values.
s = s[2:]
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
出力は以下の通りです。
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]
スライスのデフォルトはnilです。
また、それとは別で空のスライスを作ることもできます。
// これはnil
var s []int
// これは空のスライス
s := []int{}
組み込みのmake関数を使ってスライスの動的生成が可能です。
ここで少しlenとcapの違いを理解しました。(コード内のコメントを参照)
package main
import "fmt"
func main() {
a := make([]int, 5)
printSlice("a", a)
// 何も入っていないけど、容量は5個分あるスライスを生成
// nilではないけど中身は空のスライス
b := make([]int, 0, 5)
// 出力は[]
printSlice("b", b)
// 0 - 1のスライスを取得
// この時初めて中身のintのデフォルト値がスライス内に配置される
c := b[:2]
// 出力は[0, 0]
printSlice("c", c)
// 2 - 5のスライスを取得
d := c[2:5]
// 出力は[0, 0, 0]
printSlice("d", d)
}
func printSlice(s string, x []int) {
fmt.Printf("%s len=%d cap=%d %v\n",
s, len(x), cap(x), x)
}
append関数でスライスに要素を追加することができます。
package main
import "fmt"
func main() {
var s []int
printSlice(s)
// append works on nil slices.
s = append(s, 0)
printSlice(s)
// The slice grows as needed.
s = append(s, 1)
printSlice(s)
// We can add more than one element at a time.
s = append(s, 2, 3, 4)
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
for文でrangeを使うことで反復できます。
package main
import "fmt"
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
for i, v := range pow {
// iはインデックス、vは要素のコピー
fmt.Printf("2**%d = %d\n", i, v)
}
// 要素のコピーを無視したい場合はiだけでもOK
for i := range pow {
pow[i] = 1 << uint(i) // == 2**i
}
// インデックスを無視したいならアンダーバーを使う
for _, value := range pow {
fmt.Printf("%d\n", value)
}
}
まとめ
今日は疲れたのでここまで。
Go面白い。