Shoichi Matsuda's diary

このブログは移転しました。 https://shoma2da.net/ が新しいブログです。

初めてのGo言語(golang)

突然新言語を書きたくなってGoを調べてみました。

Goってどんな言語?

Googleが開発した言語で2009年に登場しました。
公式サイトはThe Go Programming Languageです。
チュートリアルには日本語も用意されているので英語が苦手な方でもすぐに着手できます。

コンパイル型の言語だそうです。パラダイムはよくわからず...。

Goを書き始める

インストール

Macの方はHomebrewで一発です。

$ brew install go
$ go version
go version go1.3 darwin/amd64

そのほかのOSやMacだけどHomebrewを使わない、といった方はこちらを参照してください。

Hello World

Test.goファイルを以下の内容で作成してgoコマンドで実行します。

package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}
$ go run Test.go 
hello, world

できました。

チュートリアルをひと通りやってみる

チュートリアルはこちらです。
目に止まった特徴的だと感じたものを書き残しておきます。

Multiple results

タプル、と呼ばれたりするものですね。今どきの言語っぽいです。

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 results

戻り値に変数名をつけて関数内では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))
}
Short variable declarations

関数内で使える暗黙的な型宣言らしいです。
型推論が充実すればこの書き方をする必要がないような...。今のところメリットはよくわかってません。

package main

import "fmt"

func main() {
    k := 3

    fmt.Println(k)
}
Slicing slices

配列の中の値をスライスして取得できます。これはなかなか便利そう。

package main

import "fmt"

func main() {
    p := []int{2, 3, 5, 7, 11, 13}
    fmt.Println("p ==", p)
    fmt.Println("p[1:4] ==", p[1:4])

    // missing low index implies 0
    fmt.Println("p[:3] ==", p[:3])

    // missing high index implies len(s)
    fmt.Println("p[4:] ==", p[4:])
}
Function values

今どきの言語っぽく関数はファーストクラスのようです。

package main

import (
    "fmt"
    "math"
)

func main() {
    hypot := func(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    }

    fmt.Println(hypot(3, 4))
}
Goroutines

goキーワードを使って軽量スレッド上で実行できるそうです。
簡単でなかなか良さそうですね。

package main

import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("world")
    say("hello")
}
Channels

チャネル、という仕組みでスレッドの待ち合わせが簡単にできるみたいです。

package main

import "fmt"

func sum(a []int, c chan int) {
    sum := 0
    for _, v := range a {
        sum += v
    }
    c <- sum // send sum to c
}

func main() {
    a := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)
    x, y := <-c, <-c // receive from c

    fmt.Println(x, y, x+y)
}

所感

ささっとチュートリアルを見たぐらいなので、かなり浅い考察しかできていませんが今の思いを正直に書いていきます。

  • 型推論がいまいちすぎる??
  • ポインタ出てきた!?後発言語で見るのは逆に新鮮!
  • Map、配列の宣言があんまりきれいじゃないような...
  • 関数がファーストクラスだけど、それ以外に関数型らしさ(定数とか遅延評価的なもの)はほとんどない?
  • クラスの仕組みがないのにびっくり!(structにメソッドを定義する)
  • スレッドの扱いはかなり楽そう
  • 売りはどこなんだろう??スレッド?

こんな感じで第一印象はいまいちです...。
(パラダイムなど下調べをほとんどせずに書いているのも良くないですね...)

ただ、もう少し深く見ていけばオブジェクト指向や関数型の考え方からは少し離れた設計技法があるなど実はかなり奥が深い言語だったりするのかな?といった期待もあります。
今後も少しずつ調べていこうと思います!