Help us understand the problem. What is going on with this article?

ざっくり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
finc
健康寿命を伸ばすアプリFiNCの開発・運営を行うモバイルヘルステクノロジーベンチャー
https://finc.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away