Go 言語の基本文法

シンプルかつ処理速度が速いプログラミング言語である Go について基本文法をまとめてみました。
私はコンパイル速度が速いこと、及びシングルバイナリができることが気に入っています。
思い出したりするときのために作ったので参考までに見てみてください。
学習サイトは以下が便利です。

ちなみに簡単に試すときはこの辺が便利です。


1.パッケージ

ディレクトリ(フォルダ)にファイルを分けた時は、以下のようにディレクトリ名をコードの最初に記載します。(foo フォルダにある場合)

package foo

2.エントリーポイント

main パッケージの関数main からプログラムが始まります。

package main
func main(){
}

3.コメント

処理には関係ないコメントは以下の2通りで書けます。

// コメント
/*
複数行
*/

4.インポート

パッケージを使用するときに宣言します(fmt パッケージを使用したケース)

//ひとつ
import "fmt"

//複数
import (
    "fmt"
    "string"
)

5.変数

1.明示的な定義

var 変数名 型 が基本的な構文です。変数=値で代入できます。

var x,y,z int
x,y,z=1,2,3

2.複数の異なる型をまとめて定義

   var(
        x,y int
        id string
        )

3.型推論を利用した宣言+代入

i:=5

6.基本型

1.論理値型

var a bool
b:=false

2.数値型

〇 符号付き(int)と符号なし(uint)の型とビット数(8,16,32,64)の組み合わせから成り立ちます。ビット数が無い場合は実数依存です。
〇 リテラルは123(10進数)、0123(8進数)、0x07(16進数)といったものがあります。
〇 型変換は型(値)で変更します。

a:=12.0
b:=uint(a)

3.浮動小数点型

〇 浮動小数点型(float)とビット数(32,64)の組み合わせから成り立ちます。
リテラルとしては3.14、1.5e-2 といった表現方法があります。

a:=1.25e-5
fmt.Printf("%T\n", a)

4.複素数型

〇 complex64 と complex128 の型があります。

   var i complex64
   i=1+4i

5.rune型

〇 文字コードを扱う型です。string(runeの値)で文字に戻るので以下のように使用できます。

s:="test"
for _,r:=range s{
  fmt.Println(string(r))
}

6.文字列型

〇 string 型が存在します。複数行にわたる文字列を書くときは、RAW文字列を使用します。

 var s string
 s="おはよう"
 fmt.Println(s)
 
 text:=`
 隣の客は
 よく
 柿食う客だ
 `
 
 fmt.Println(text)

7.配列型

サイズの変更はできず同じ型のデータが入っている型です。

//要素のサイズが3
a:=[3]int{1,2,3}
//宣言だけすると初期値が入っている
var b [4]int
//代入
a[0]=0
fmt.Println(a)
//出力
fmt.Println(b[1])

8.interface型

どんな型のデータでも入る型ですが、計算等はできなくなります。

//宣言しただけだと nil という値のない値になります
var i interface{}
i="test"

7.演算子

代表的なものとして、
算術(+ , - , * , / , % , ビット演算系)、
比較(== , != , > , >= , < , <=)、
論理(&& , || )演算子があります。

8.定数

〇 変化しない値を使用するときは定数を使用します。

const A=1
//複数定義して省略すると全て同じ値
const (
    X=1
    Y
    Z
    )

〇 型を明示するとき

const A=int64(1)

〇 iota は1つずつ増分する

const (
    X=iota //0
    Y     //1
    Z    //2
    )

9.関数

〇 基本形は 「func 関数名(引数の内容) 戻り値型」となり、引数を使用しない場合や戻り値を使用しない場合は省略します。

複数の戻り値はタプルで表現し、戻り値で使用しない場合は「_」で無視できます。

func swap(x,y string) (string,string){
    return y,x
}

func main(){
head,_:=swap("Aさん","Bさん")
fmt.Println(head) 
}

エラー処理は以下のように行うのが Go の代表的な書き方です。

result,err:=do()
if(err!=nil){
//エラー処理
}

〇 無名関数は関数名を省略します。

f:=func(x,y int) int{return x*y}
fmt.Println(f(2,3)) //6

〇 関数を引数に取ることもできます。

package main
import "fmt"

func output(f func(int,int) int, x int,y int) bool{
    result:=f(x,y)
    fmt.Println(result)
    return true
}

func main(){
    multi:=func(x,y int)int{return x*y}
    ok:=output(multi,3,4) //12を出力し、trueを返す
    fmt.Println(ok) //trueを出力
}

〇 戻り値を関数にすることもでき、無名関数のクロージャーの性質を活かし、ジェネレーターが実装できます。呼び出すたびにカウントダウンする関数の例です。

func countDown() func() int{
    count:=10
    return func() int{
        res:=count
        count-=1
        return res
    }
}

func main(){
    f:=countDown()
    fmt.Println(f())//10
    fmt.Println(f())//9
    //戻り値が関数のものは()追加で実行できる。別のジェネレーターになるため別カウント
    fmt.Println(countDown()())//10    
}

10.制御構文

1.if文

条件分岐は以下のように記載します。

func main(){
    x:=10
    //条件式
    if x==1{
        fmt.Println("1です")
    }else if x==2{
        fmt.Println("2です")
    }else{
        fmt.Println("それ以外です")
    }
    //単一文の追加可能
    if a,b:="A","B";a==b{
        fmt.Println("同じ")
    }else{
        fmt.Println("異なる")
    }
    
}

2.for文

繰り返し文は以下のように記載します。
〇 無限ループ(break文と併用したケース)

  count:=0
    for{
        //10回で終了(break)
        if count>10{
            break
        }
        
        fmt.Println(count)
        count++
    }

〇 条件付きfor文(while文)

    count:=0
    for count<10{
        fmt.Println(count)
        count++
    }

〇 回数for文 (continue文と併用したケース)

    for i:=0;i<10;i++{
        //偶然回skip
        if (i%2==0){
            continue
        }
        fmt.Println(i)
    }

〇 取り出し系for文

    var cities [3]string
    cities=[3]string{"Tokyo","Pari","London"}
    
    for i,v:=range cities{
        fmt.Println(i,":",v)
    }

3.switch 文(break が不要)

〇 一般的なswitch文

    val:=3
    //値switch
    switch val{
        case 1,2,3:
        fmt.Println("1から3です")
        default:
        fmt.Println("それ以外です")
    }
    
    //条件switch
    switch {
        case val%2==0:
        fmt.Println("偶数")
        case val%2==1:
        fmt.Println("奇数")
        default:
        fmt.Println("それ以外")
    }

〇 型switch

    var val interface{}=3
    //型switch
    switch v:=val.(type){
        case bool:
        fmt.Println(v)
        case int:
        fmt.Println(2*v)
        default:
        fmt.Println("それ以外")
    }

4.特殊な文(defer)

リソースの開放などに使用され、関数の終了前に実行されます。

   defer fmt.Println("defer")//最後に実行
   fmt.Println("Hello")

5.特殊な文(go)

並列処理が実行されます。

    //定義された関数や無名関数を並列処理できる
   go func(x int){
       for x<10{
           fmt.Println("並列処理",x)
           x++
       }
   }(1)
   
   for i:=0;i<10;i++{
       fmt.Println("Main処理",i)
       time.Sleep(500 * time.Millisecond)
   }

11.参照型

1.スライス

可変長な配列です。
〇 生成、代入、参照

    //宣言 nil である
    var a []int
    //make による生成。 cap の値は省略可
    s:=make([]int,5,10)
    fmt.Println(len(s)) //5
    fmt.Println(cap(s)) //10

    //リテラル
    s:=[]int{1,2,3,4,5,6}
    
    //代入
    s[2]=10

    //参照
    fmt.Println(s[2]) //10

〇 簡易スライス式

    //リテラル
    s:=[]int{1,2,3,4,5,6}
    
    //簡易スライス式(末尾は含まない)
    a:=s[2:4]
    for _,v:=range a{
        fmt.Println(v) //3,4
    }

〇 要素の末尾追加とコピー

    //リテラル
    s:=[]int{1,2,3}
    
    //末尾追加
    s=append(s,4,5,6)
    fmt.Println(s) //[1 2 3 4 5 6]

    //コピー
    a:=make([]int,len(s))
    copy(a,s)
    fmt.Println(a)

〇 可変長引数

func output(s ...string){
    for _,v:=range s{
        fmt.Println(v)
    }
}

func main(){
    //使用方法
    output("A1","B1","C1")
    
    //使用方法2
    s:=[]string{"A2","B2","C2"}
    output(s...)
}

2.マップ

マップは連想配列(辞書)と同じ使い方をするものです。
〇 宣言、生成、代入、参照

    //宣言。この時点では nil
    var m map[int]string
    //生成
    m1:=make(map[int]string)
    //代入 存在すれば上書き
    m1[1]="A"
    m1[2]="B"
    //参照
    fmt.Println(m1[2])
    //参照方法2
    _,ok:=m1[9]
    fmt.Println(ok) //false

〇 リテラル、for文

    //リテラル
    m:=map[int]string{1:"東京",
        2:"大阪",
        3:"名古屋",
    }
    //サイズ
    fmt.Println(len(m))
    //削除
    delete(m,2)

    //for文。順不同
    for k,v:=range m{
        fmt.Printf("%d %s\n",k,v)
    }

3.ポインタ

メモリ上のアドレスと型の情報を入れる型です。

    //宣言 nilになる
    var p *int
    var i int
    //代入
    p=&i
    i=2
    //デリファレンス i=5になる
    *p=5

〇 配列を引数にして、引数に入れた配列を2倍にするコード例

func double(a *[3]int){
    i:=0
    for i<3{
        //(*a)[i]でも可
        a[i]=2*a[i]
        i++
    }
} 

func main(){
    a:=[3]int{1,2,3}
    //[2 4 6]
    double(&a)
}

4.チャネル

非同期処理のデータの受け渡し用のデータ型です。
〇 宣言、生成、データの送受信

    //宣言
    var ch chan int
    //受信専用(チャネルから受信)
    var ch1 <-chan int
    //送信専用(チャネルへ送信)
    var ch2 chan<- int

    //生成(数字はバッファーサイズ)
    ch:=make(chan int,5)
    //チャネルはデータを受け取る
    ch<-5
    //チャネルはデータを渡す
    i:=<-ch

〇 これ以上送信する値がないときは close できます。closeしているチャネルに送信するとpanicになります。rangeでチャネルを使用するとき等にcloseして、終了をしらせることができます。

//矢印の省略可
func fibonacci(n int, c chan<- int) {
	x, y := 0, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x+y
	}
	close(c)
}

func main() {
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	for i := range c {
		fmt.Println(i)
	}
}

〇 select は、複数ある case のいずれかが準備できるようになるまで待ち、 複数の case の準備ができている場合、 ランダムに選択されます。

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:	
			fmt.Println("quit")
			return
		}
	}
}

12.構造体

複数の型をまとめて1つにしたものです。
〇 定義、宣言、値の代入、参照、複合リテラル

//structで定義した情報にtypeでエイリアスを付与
type User struct{
    Name string
    Age int
}

func main(){
    //宣言 全ての型に初期値が入っている
    var u User
    //代入
    u.Name="A"
    //参照
    fmt.Println(u.Name)
    //複合リテラル
    u2:=User{Name:"B",Age:30}
}

〇 構造体を含む構造体

type Address struct{
    Code int
}

type User struct{
    Name string
    Address Address
}

func main(){
    //生成
    u:=User{
        Name:"A",
        Address:Address{
            Code:100,
        },
    }
    //参照 100
    fmt.Println(u.Address.Code)
}

〇 構造体のポインタ生成<構造体は値渡し>

    //生成方法1
    p:=new(User)
    //生成方法2
    p1:=&User{Name:"A"}

〇 メソッドとコンストラクタの代わり

type Rectangle struct{
    Height int
    Width int
}

//コンストラクタの代わり
func NewRectangle(h,w int) *Rectangle{
    return &Rectangle{
        Height:h,
        Width:w,
    }
}

//面積計算のメソッド
func (r *Rectangle) Area() float64{
    return float64(r.Height)*float64(r.Width)
}


func main(){
    r:=NewRectangle(5,4)
    fmt.Println(r.Area())
    //マップ
    m:=map[Rectangle]string{
        {Height:5,Width:4}:"test1",
        {Height:10,Width:3}:"test2",
    }
    for k,v:=range m{
        fmt.Println(k.Area(),v)
    }
}

〇 インターフェース
どのインターフェースを実装するか宣言する必要はありません。

type Animal interface{
    Bark()
    GetName() string
}

type Dog struct{
    Name string
}

type Cat struct{
    Name string
}

func (d *Dog) Bark(){
    fmt.Println("ワンワン")
}

func (d *Dog) GetName()string{
    return d.Name
}

func (c *Cat) Bark(){
    fmt.Println("ニャー")
}

func (c *Cat) GetName()string{
    return c.Name
}

func main(){
    animals:=[]Animal{
        &Dog{Name:"タロー"},
        &Cat{Name:"にゃん太"},
    }
    
    for _,v:=range animals{
        fmt.Println(v.GetName())
        v.Bark()
    }
}


この記事が気に入ったらサポートをしてみませんか?