概要
復習も兼ねて、Go言語の文法を簡単にまとめた。
環境設定は以下を参照。
http://qiita.com/chrischris0801/items/ce1d3a493750f67167ec
基本
// コメントは「//」で書く
// Go言語のソースファイルは必ずpackageで始まる
package main
// fmtパッケージをインポートする
import "fmt"
// main関数
func main() {
fmt.Println("main")
}
package
Go言語のソースファイルはなんらかのpackageに属さなければいけない。
多数のソースファイルのうちの少なくとも1つはmainパッケージに属させないといけない。
import
外部のソースやパッケージを利用する際には import
で追加する。
func
関数の宣言は func
によって行う。
ソースコードの実行
コマンドラインで $ go run filename
と打つと、ソースファイルのコンパイルと実行を同時に行ってくれる。
$ go run main.go
main
変数
変数を宣言するときは型宣言もする。
// 方法1; 宣言する段階で値が決まっていない場合
var msg_1 string
msg_1 = "Hello, world"
// 方法2
var msg_2 = "Hello, world" // 初期化に使った型になる
// 方法3; 宣言する段階で値が決まっている場合
msg_3 := "Hello, world" // 宣言と代入を一気に行う
型
ゼロ値
変数宣言した時に値を指定しなかった場合、その変数には「ゼロ値」が設定される。
- int: 0
- float64: 0.0
- string: ""
- bool: false
- Array: 各要素がゼロ値の配列
- 構造体: 各フィールドがゼロ値の構造体
- その他の型: nil
型変換
型の変換は以下のように行える。
// 変数の宣言
var i int = 1234
// intからstringへの変換
var s string = string(i)
演算子
演算子は他の言語と同じように、
- 算術演算子: + - * /
- 代入演算子: += -=
- 比較演算子: == != < <= > >=
- 論理演算子: && || !
- インクリメント/デクリメント: ++ --
が使える。
Array
GoにはArrayとSliceがあり、Arrayは要素数が固定されているがSliceは要素数が固定されていない。
// [要素数]type{中身}
a := [3]int{1, 3, 5}
// 要素数は以下のように省略できる
b := [...]int{1, 3, 5}
型はいずれの場合も [3]int
となる。
Arrayは長さ(length)と要素(element)の型を明らかにしたものであり、例えば[2]int
と[3]int
では違う型である。
Slice
SliceはArrayの要素数の規定が無いもので、長さ(len
)と最大収容数(cap
)を持つ。
// 方法1
s1 := []int{1, 3, 5}
len(s1) // 3
// 方法2; makeを使う
// capを省略すると、len == capとなる
s2 := make([]int, 3) // ゼロ値は[0,0,0]
// スライスの末尾に要素を追加
s1 = append(s1, 8, 2, 10)
マップ
keyもvalueも型を指定する。
// 変数の宣言
m := map[string]int{"kurisu":100, "ken":200}
// keyが"kurisu"の要素を削除
delete(m, "kurisu")
// 値を取り出す
// errの値も取り出す
value, err := m["ken"]
fmt.Println(value) // 200
fmt.Println(err) // nil
制御構文
if
point := 60;
if score > 80 {
fmt.Println("Excellent")
} else if score > 60 {
fmt.Println("Good")
} else {
fmt.Println("Bad")
}
switch/case
default
句でいずれの場合(case
)にも該当しなかった場合の処理を書きます。
signal := "blue"
switch signal {
case "red":
fmt.Println("Stop")
case "yellow":
fmt.Println("Caution")
case "green", "blue":
fmt.Println("Go")
// どのcase節にも一致しなかったときに実行される処理
default:
fmt.Println("wrong signal")
}
$ go run switch.go
Go
for
単純な繰り返し処理では、for文を使います。
ループの終了には break
を使い、スキップには continue
を使う。
for i := 0; i < 10; i++ {
// break: ループ終了
if i == 3 { break }
// fmtパッケージのPrintln関数
fmt.Println(i)
}
range
Sliceやマップのループ処理には range
を使う。
s := []int{2, 3, 8}
// インデックスと要素を取り出す
for i, v := range s {
fmt.Println(i, v)
}
// 要素のみを取り出したい場合
for _, v := range s {
fmt.Println(v)
}
マップは以下のようにkeyとvalueを取り出す。
m := map[string]int{"taguchi":200, "fkoji":300}
// key, value
for k, v := range m {
fmt.Println(k, v)
}
構造体とメソッドの定義
以下は四角形の構造体を定義し、面積を算出するメソッドを定義しているコード。
package main
import "fmt"
// 構造体の定義
// type name struct { ...フィールド... }
type rect struct {
width int
height int
}
// メソッド; レシーバ付きの関数
// func (レシーバの変数名 レシーバの型) メソッド名(パラメータリスト) 戻り値の型 {
// # 処理
// return 戻り値
// }
func (r *rect) area() int {
return r.width * r.height
}
// mainで実行する
func main() {
// 構造体の初期化
r := rect{width: 10, height: 5}
fmt.Println("area: ", r.area())
}
$ go run struct.go
50
ポインタ
ポインタとは「あるメモリ領域を指し示すデータ」である。
変数を宣言すると、特定のメモリ領域に保存され、ポインタはその値を示すことになる。
ポインタを使うときは *
と記述する。
レシーバの値を更新するようなメソッドでは、レシーバはポインタにしなければいけない。
レシーバにポインタを使うと、変数(構造体)のフィールドの値を変更することができるが、使わないと変更はもとの変数に反映されない。
// int型から新たにmyType型の構造体を宣言
type myType int
// レシーバが値(非ポインタ)のメソッド
func (value myType) setByValue(newValue myType) {
value = newValue
}
// レシーバがポインタのメソッド
func (value *myType) setByPointer(newValue myType) {
*value = newValue
}
func main() {
var x myType = 0
x.setByValue(1)
fmt.Println(x) // xは0のまま
x.setByPointer(1)
fmt.Println(x) // xが1になる
}
コンポジション
構造体の中に構造体を埋め込むことができる。
package main
import "fmt"
type Person struct {
Name string
Address Address
}
type Address struct {
Number string
Street string
City string
State string
Zip string
}
func (p *Person) Talk() {
fmt.Println("Hi, my name is", p.Name)
}
func (p *Person) Location() {
fmt.Println("I’m at", p.Address.Number, p.Address.Street, p.Address.City, p.Address.State, p.Address.Zip)
}
func main() {
p := Person{
Name: "Steve",
Address: Address{
Number: "13",
Street: "Main",
City: "Gotham",
State: "NY",
Zip: "01313",
},
}
p.Talk()
p.Location()
}
インターフェース
PersonもCitizenも、Humanの一部であるので、Humanインターフェースを持つようにする。
type Human interface {
Talk()
}
func SpeakTo(h Human) {
h.Talk()
}
func main() {
p := Person{Name: "Dave"}
c := Citizen{Person: Person{Name: "Steve"}, Country: "America"}
SpeakTo(&p)
SpeakTo(&c)
}
Goroutineとchannel
goroutineは並行処理ができる機能で、channelは並行処理中に結果の受け渡しを行うための機能。
package main
import (
"fmt"
"time"
)
func task1(result chan string) {
time.Sleep(time.Second * 2)
// channelに結果を出力する
result<- "task1 result"
}
func task2() {
fmt.Println("task2 finished")
}
func main() {
// channel型のインスタンス
result := make(chan string)
// goroutineとして実行できる
go task1(result)
go task2()
// task1の結果を取得する
// 結果を取得できるまで待機している
fmt.Println(<-result)
time.Sleep(time.Second * 3)
}
$ go run goroutine.go
task2 finished
task1 result