SE_BOKUのまとめノート的ブログ

SE_BOKUが知ってること・勉強したこと・考えたことetc

GO言語(golang)1.20をはじめる③/最低限の文法をまとめる

f:id:arakan_no_boku:20210412005751p:plain

目次

GO言語(golang)1.20をはじめる③/最低限の文法をまとめる

前2回で、開発環境構築と「パッケージ」「モジュール」の意味を整理しました。

arakan-pgm-ai.hatenablog.com

arakan-pgm-ai.hatenablog.com

あとは、 最低限の文法を整理すれば、とりあえずプログラムは作れます。 

文法:パッケージとimport

Goのプログラムは main パッケージから開始されます。

package main

importは{}でくくって宣言するのが推奨です。

import (
    "fmt"
    "math"
)

文法:関数

関数は「func」で宣言します。

main()がまず実行されます。

func main() {
    fmt.Println(add(42, 13))

関数の引数は必ず型を宣言する必要があります。

戻り値の型も宣言が必要です。

戻り値の型(例だとint)は後ろに置きます。

func add(x int, y int) int {
    return x + y
}

関数の戻り値を複数定義する場合は、以下のようになります。

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

文法:組み込み型

基本の組み込み型です。

bool

string

int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 の別名

rune // int32 の別名

float32 float64

complex64 complex128

NULLにあたるものは

nil

です。

文法:キャスト(型変換)

Go言語での型変換は明示的な変換が必要です。

例えば、上記の「int」型を「float64」型に変換(キャスト)するには。

var i int = 42
var f float64 = float64(i)

みたいにします。

文法:変数

変数は「var」で宣言します。

外部パッケージから参照可能(public)な変数は「大文字」、パッケージ内だけしか参照できない(private)変数は「小文字」で始めます。

型は後ろに置きます。

var i int

var f float64 

型を明示的に指定しないと、右側の変数から型推論(Type inference)します。

i := 40           // intに型推論される

これを使って複数の変数をいっぺんに初期化することもできます。

var i, j int = 1, 2

変数を初期値を与えずに宣言すると以下のゼロ値が設定されます。

  • 数値型(int,floatなど): 0
  • bool型: false
  • string型: "" (空文字列( empty string ))

関数の中で、varを使わずに「:=」で暗黙的な宣言もできます。

関数内で使い捨てのカウンタみたいなものなら、これだけでもOKです。

k := 3

前に説明した型変換も、これを使って、以下のように書けます。

i := 42
f := float64(i)

文法:定数

定数はconstで宣言します。

const Pi = 3.14

定数で宣言できるのは

  • 文字(character)
  • 文字列(string)
  • boolean
  • 数値(numeric)

のみです。

定数は := を使って宣言することはできません。

文法:演算子

一覧にまとめてくれているページがあったので、リンクだけ載せておきます。

基本的な部分は他の言語とほぼ同じです。

www.twihike.dev

文法:固定長配列

固定長配列(Arrays)はサイズを指定し、型を後ろにおいて宣言します。

var a [2]string
a[0] = "Hello"
a[1] = "World"

配列は上記のように固定長のものを指します。

以下のような初期化の仕方も可能です。

arr := [...] string{"Hello", "World"}

文法:可変長配列(スライス)

サイズを指定しないで宣言するのが「スライス(Slices)」です。

var s int

何もはいっていないときは「 nil」 です。

定義後の要素の追加は、append()で行います

追加後の戻り値は「新しいスライス」になります。

new_slice = append(s, 0)

宣言時の初期化も可能です。

new_slice := int{2, 3, 5, 7, 11, 13}

pythonのスライスっぽい参照の仕方ができます

new_slice = s[1:4]

スライスの指定の仕方の簡単な例です。

  • s[start:end]  →  start から end - 1 まで
  • s[start:]   → start から最後尾まで
  • s[:end]  →  先頭から end - 1 まで
  • s[:]   →  先頭から最後尾まで

長さの確認は「len」を使います。

len(s)

文法:連想配列(map)

mapの初期化の方法は「make」を使う方法と、使わない方法があります。

makeを使う方法です。

m := make(map[string]string) 
m["first"] = "Mike" //マップにkeyとvalueを挿入する。
m["second"] = "Smith"

同じことをmakeを使わないでやるとこうなります。

var m = map[string]string{"first":"Mike", "second": "Smith"}

mapへの追加・要素の取得・削除の仕方です。

m[key] = elem

elem = m[key]

delete(m, key)

mapの要素の存在確認はちょっと変わっていて、例えば、keyが存在するかどうかは、以下のように要素の取得時に2番目に変数「ok」等を書きます。

elem, ok = m[key]

存在すればokがtrue、しなければfalseになり、okの場合はelemに値がセットされます。

文法:ループ (カウンタ利用)

forループはC言語っぽいですが、「i := 0; i < 10; i++」の部分を()で囲みません。

for i := 0; i < 10; i++ {
    sum += i
}

ループを途中で打ち切るには「break」。

スキップして次にうつるには「continue」です。

文法:ループ(スライス)

スライスやmapの場合は、rangeで取り出しながらループします。

スライスの場合は、index、valueが取得できるので、以下のようになります。

var pow = int{1, 2, 4, 8, 16, 32, 64, 128}

for index, value := range pow {
    fmt.Printf("index(%d) : value(%d)\n", index, value)
}

文法:ループ(map)

mapの場合は、key、valueを取り出します。

var m = map[string]int{ "one":   1, "two":   2}

 for key, value := range mapping {
        fmt.Println(key, value)
  }

キーのみが欲しい場合は「key,_」、valueのみなら「_,value」のように「_」で不要な部分を指定することもできます。

文法:条件分岐(IF)

if文も同様に「 x < 0」の部分を()で囲みません。

if x < 0 {
    return sqrt(-x) 
}else{

    return 0

}

文法:条件分岐(switch)

switch~case文はbreakを書かなくても、GOが自動的に補完してくれます。

t := time.Now()
switch {
case t.Hour() < 12:
    fmt.Println("Good morning!")
case t.Hour() < 17:
    fmt.Println("Good afternoon.")
default:
    fmt.Println("Good evening.")

文法:ポインタ

GO言語はポインタが使えます。

C言語っぽいですが、C言語と違ってポインタ演算はできません。

var p *int

i := 42
p = &i

文法:defer

defer に渡された関数は、呼び出し元の関数の終わり(returnする)まで実行されません。

簡単な例で書くと。

func main() {
    defer fmt.Println("world")

    fmt.Println("hello")
}

のように書かれたものを実行すると「fmt.Println("hello")」が先に実行されて、deferに渡された「fmt.Println("world")」はmain()関数の終わりに実行されます。

なので。

hello
world

と結果が表示されます。

これはエラー処理などで、とても役にたちます。

defer へ渡した関数が複数ある場合スタック( stack )されて「 LIFO(last-in-first-out) 」の順番で実行されます。

文法:構造体

struct(構造体)は以下のように定義します。

type Vertex struct {
    X int
    Y int
}

構造体の初期化はをつかっておこない、メンバには、ドット(.)でアクセスできます。

v := Vertex{1, 2}
v.X = 4

GO言語にはクラスはありません。

ですが、型にメソッドを定義するなどして、クラスっぽいことを実現する方法はあるみたいです・・ただ、そのへんに踏み込むと「最低限」ではなくなるので、そこは別の機会にまとめることにします。

文法:コメント

// と /* */が使えます。

//以降の文字列をコメントとします。

こちらは1行コメントです。

/*  */で囲まれた部分をコメントとします。

/* */では複数行のコメントを書くことができます。

まとめ:簡単なコードを書いて試してみる

動作確認用に簡単なコードを書いてみました。

package main

import (
	"fmt"
	"time"
)

func main() {
	var mapd = make(map[string]string)
	var now = time.Now()
	var layout = "2006/01/02"
	mapd["今日"] = now.Format(layout)
	var tomorrow = now.AddDate(0, 0, 1)
	mapd["明日"] = tomorrow.Format(layout)
	var nextmonth = now.AddDate(0, 1, 0)
	mapd["一月後"] = nextmonth.Format(layout)
	for key, value := range mapd {
		if key != "今日" {
			fmt.Printf("%sは%sです\n", key, value)
		} else {
			fmt.Printf("%sは%sです\n", value, key)
		}
	}
	switch {
	case now.Hour() < 12:
		fmt.Println("Good morning!")
	case now.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}
	new_slice := []int{2, 3, 5, 7, 11, 13}
	fmt.Println(new_slice[2:4])
}

実行タイミングによって変わりますけど、こんな結果が表示されます。

2023/06/03は今日です
明日は2023/06/04です
一月後は2023/07/03です
Good evening.
[5 7]

いけてますね。

最低限の文法としては、こんなもんかと思います。

ではでは。