Posted at

ざっくりGoの文法まとめ

More than 3 years have passed since last update.


概要

復習も兼ねて、Go言語の文法を簡単にまとめた。

環境設定は以下を参照。

http://qiita.com/chrischris0801/items/ce1d3a493750f67167ec


基本


main.go

// コメントは「//」で書く

// 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)にも該当しなかった場合の処理を書きます。


switch.go

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 を使う。


slice.go

s := []int{2, 3, 8}

// インデックスと要素を取り出す
for i, v := range s {
fmt.Println(i, v)
}

// 要素のみを取り出したい場合
for _, v := range s {
fmt.Println(v)
}


マップは以下のようにkeyとvalueを取り出す。


map.go

m := map[string]int{"taguchi":200, "fkoji":300}

// key, value
for k, v := range m {
fmt.Println(k, v)
}



構造体とメソッドの定義

以下は四角形の構造体を定義し、面積を算出するメソッドを定義しているコード。


struct.go

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は並行処理中に結果の受け渡しを行うための機能。


goroutine.go

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