Go
golang

【Go】基本文法総まとめ


概要

【Go】基本文法①(基礎)

【Go】基本文法②(フロー制御文)

【Go】基本文法③(ポインタ・構造体)

【Go】基本文法④(配列・スライス)

【Go】基本文法⑤(Maps・ Range)

【Go】基本文法⑥(インターフェース)

【Go】基本文法⑦(並行処理)

自身の学習記事を参考に、Goの基礎文法をポイントごとにより簡潔にまとめました。

各単元の詳細説明は上記の記事を参照ください。


パッケージ

パッケージ(package)ポイント


  • Goのプログラムはパッケージ(package)によって成り立っている。

  • importを使用する事で必要なパッケージ(package)をインポートする事が可能。

  • Goでは最初の文字が大文字で始まるものは、外部パッケージから参照が可能(Exported names)。
    (例えば以下のPiはmathパッケージからエクスポートされたもの。)

参考コード

package main

import (
"fmt"
"math"
)
//import "fmt"
//import "math" の様に個別でもimportが可能

func main() {
fmt.Println(math.Pi) //=> 3.141592653589793
}


Functions(関数)

Goでは関数は以下の様に定義される。

参考コード

func  <関数名>([引数]) [戻り値の型] {

[関数の本体]
}

Functions(関数)のポイント


  • 引数の戻り値の型を書く必要がある。(ex1)

  • 関数が2つ以上の同種類の引数を伴う場合、以下の様に型の省略する事が可能。(ex2)

  • 関数は複数の値を返すことができる。(ex3)

参考コード

func main() {

fmt.Println(hello("Hello World")) //=> Hello World
fmt.Println(hello2("Hello", "World")) //=> Hello World
fmt.Println(multipleArgs("Hello", "World")) //=> World Hello
}
//ex1:引数の戻り値の型を書く必要がある
func hello(arg string) string{
return arg
}
//ex2:関数が2つ以上の同種類の引数を伴う際、型の省略する事が可能
func hello2(arg1, arg2 string) string {
return arg1 + " " + arg2
}
//ex3:関数は複数の値を返すことができる
func multipleArgs(arg1, arg2 string)(string, string){
return arg2, arg1
}


Variables(変数)

Variables(変数)ポイント



  • varによって変数を宣言し、型の明示が必要である。(ex1)

  • 初期値を渡した状態で変数を宣言すると型の明示を省略が可能。(ex2)

  • 関数内では:=を利用することでより短いコードで変数の宣言を行うことが可能。(ex3)

参考コード

//ex1:`var`によって変数を宣言し、型の明示が必要である。

var lang string
//※変数に初期値を与えないとゼロ値(Zero values)が設定されます。(数値型には0、bool型にはfalse、string型には""(空の文字列)が与えられる。

//ex2:初期値を渡した状態で変数を宣言すると型の明示を省略が可能。
var lang2 = "Golang"//初期値を渡す。

func main() {
//ex3:関数内では`:=`を利用することでより短いコードで変数の宣言を行うことが可能
lang3 := "JS"
fmt.Println(lang, lang2, lang3) //=> Golang JS
}


Constants(定数)

Constants(定数)ポイント


  • 定数は、constキーワードを使用して宣言する。

  • 定数は、文字(character)文字列(string)boolean数値(numeric)のみで使用可能。

  • 定数は:=を使用して宣言することはできない。

参考コード

const Num = 2

func main(){
const Greetings = "Hello World"
fmt.Println(Greetings) // => Hello World
fmt.Println(Num) // => 2
}


Forループ

Forループポイント


  • 使用時に条件式等を囲む()は必要ありませんが処理を囲む{}は必要。

参考コード

func main(){

sum := 0
for i := 0; i < 5; i++ {
sum += i
}
fmt.Println(sum) //=> 10
}


If(条件分岐)

ifステートメントは、forのように、条件の前に、評価するための簡単なステートメントを書くことができる。(ここで宣言された変数は、ifのスコープ内だけで有効。)

参考コード

package main 

import "fmt"

func condition(arg string)string{
if v := "GO"; arg == v {
return "This is Golang"
}else{
return "This is not Golang"
}
}
func main(){
fmt.Println(condition("Swift")) //=> This is not Golang
}


Switch(条件分岐)

Switch(条件分岐)ポイント


  • 選択されたcaseだけを実行してそれに続く全てのcasedefaultは実行されない。


  • switchの前に何も条件を書かない場合はswitch trueと書くのと同じ。

参考コード

func main(){

fmt.Println(condition("Go")) //This is Go
}

func condition(arg string) string{
switch arg {
case "Ruby":
return "This is Ruby"
case "Go": //これ以降のcaseやdefaultは実行されない。
return "This is Go"
case "JS":
return "This is JS"
default:
return "I don't know what this is"
}
}


Defer(遅延実行)

deferへ渡した関数の実行を呼び出し元の関数の終わり(returnする)まで遅延させる。

Defer(遅延実行)ポイント


  • deferへ渡した関数が複数ある場合、その呼び出しはスタックされ、新しいデータ→古いデータの順番で実行される。(=最初にdeferされた行が一番最後に実行される。)

参考コード

package main 

import "fmt"

func main(){
defer fmt.Println("Golang") //defer1
defer fmt.Println("Ruby") //defer2
fmt.Println("JS")
//=> JS
//=> Ruby
//=> Golang
}


ポインタ

ポインタ(pointer)ポイント


  • ポインタはメモリのアドレス情報のこと。

  • Goでは&を用いることで変数のアドレスの取得が可能。(ex1)


  • *を使用する事でポインタを値にとったポインタ変数の宣言が可能。(ex2)

  • ポインタ型変数名の前に*をつけることで変数の中身へのアクセスが可能。(ex3)

参考コード

func main(){

var lang string
lang = "Go"
//ex1:`&`を用いることで変数のアドレスの取得が可能
fmt.Println(&lang) //=> 0x1040c128

//ex2:`*`を使用する事でポインタを値にとったポインタ変数の宣言が可能
var lang_p *string
lang_p = &lang
fmt.Println(lang_p)//=> 0x1040c128

//ex3:ポインタ型変数名の前に`*`をつけることで変数の中身へのアクセスが可能
fmt.Println(*lang_p) //=> Go
}


Structs(構造体)

Structs(構造体)ポイント



  • classに似た役割を提供する。(関連する変数をひとまとめにする。)

  • typestructを使用して定義する。(参考コード① ex1)

  • 複数の初期化方法が存在する。(参考コード① ex2)

  • 構造体内にメソッドmethodを定義できる。(参考コード② ex3)

  • 継承に似た機能として構造体の埋め込みが可能(参考コード③ ex4)

参考コード①

func main(){

//ex2:複数の初期化方法が存在する
//初期化方法①:変数定義後にフィールドを設定する
var mike Person
mike.name = "Mike"
mike.age = 23

//初期化方法②: {} で順番にフィールドの値を渡す
var bob = Person{"Bob", 35}

//初期化方法③:フィールド名を : で指定する方法
var sam = Person{age:89, name: "Sam"}

fmt.Println(mike, bob, sam) //=> {Mike 23} {Bob 35} {Sam 89}
}

参考コード②

//ex3:構造体内にメソッドを定義できる

//普通の関数と違うのはレシーバ引数(下記の「(ele Person)」)の部分だけ。
func(ele Person)intro(arg string) string {
return arg + " I am" + " " + ele.name
}

func main(){
bob := Person{"Bob", 85}
fmt.Println(bob.intro("Hello!")) //=>Hello! I am Bob
}

参考コード③

type Person struct {

name string
age int
}

func(ele Person)intro(arg string) string { //Personのメソッド
return arg + " I am" + " " + ele.name
}

//ex4:継承に似た機能として構造体の埋め込みが可能
//構造体UserにPersonを組み込む。
type User struct {
Person
}

func(ele User)intro(arg string) string { //Userのメソッド
return "User No." + arg + " " + ele.name
}

func main(){
bob := Person{"Bob", 85}

var user1 User
//組み込みによりnameの定義が可能
user1.name = "Bob"

fmt.Println(bob.intro("Hello!")) //=> Hello! I am Bob
fmt.Println(user1.intro("1")) //=> User No.1 Bob
}


Arrays(配列)

Arrays(配列)ポイント


  • 配列とは、同じ型を持つ値(要素)を並べたもの。(ex1)

  • 複数の宣言方法がある。(ex2)

  • 最初に宣言した配列のサイズを変えることはできない。(ex3)

配列の基本的な宣言方法

 var 変数名 [長さ]

var 変数名 [長さ] = [大きさ]{初期値1, 初期値n}
変数名 := [...]{初期値1, 初期値n}

参考コード

//ex1:配列とは、同じ型を持つ値(要素)を並べたもの

//ex2:複数の宣言方法がある

//宣言方法(1)
var arr1 [2]string
//宣言方法(2)
var arr2 [2]string = [2]string{"Golang", "Ruby"}
//宣言方法(3)
var arr3 = [...]string{"Golang", "Ruby"}

func main(){
arr1[0] = "Golang"
arr1[1] = "Ruby"
fmt.Println(arr1, arr2, arr3) //=> [Golang Ruby] [Golang Ruby] [Golang Ruby]
}


Slices(スライス)

Slices(スライス)ポイント


  • 配列とは異なり長さ指定の必要なし。(参考コード① ex1)

  • 別の配列から要素を取り出し参照する形での宣言やmake()を利用した宣言が可能。(参考コード① ex2)

  • 配列とは異なり要素の追加が可能。(参考コード① ex3)


  • 長さ(length)容量(capacity)の両方を持っている。(参考コード② ex4)

  • 型が一致している場合、他のスライスに代入することが可能。(参考コード② ex5)

  • スライスのゼロ値はnil。(参考コード② ex6)

参考コード①

func main(){

//参照用配列
var arr[2]string = [2]string{"Ruby","Golang"}

//ex1:配列とは異なり長さ指定の必要なし
var slice1 []string //スライス(1)
var slice2 []string = []string{"Ruby", "Golang"} //スライス(2)

//ex2:配列から要素を取り出し参照する形での宣言が可能
var slice3 = arr[0:2] //スライス(3)

//ex2:make()を利用した宣言が可能
var slice4 = make([]string,2,2) //スライス(4)

//ex3:配列とは異なり要素の追加が可能
//append は新しいスライスを返すことに注意
slice5 := [] string{"JavaScript"}
newSlice := append(slice5, "Ruby") //sliceに"Ruby"を追加

fmt.Println(slice1,slice2,slice3,slice4, slice5, newSlice) //=>[] [Ruby Golang] [Ruby Golang] [ ] [JavaScript] [JavaScript Ruby]
}

参考コード②

func main(){

slice := []string{"Golang", "Ruby"}

//ex4:長さ(length)と容量(capacity)の両方を持っている。
//長さ(length) は、それに含まれる要素の数
//容量(capacity) は、スライスの最初の要素から数えて、元となる配列の要素数
fmt.Println(len(slice)) //=> 2
fmt.Println(cap(slice)) //=> 2

//ex5:型が一致している場合、他のスライスに代入することが可能。
var slice2[]string //sliceと同型slice2を作成
slice2 = slice //sliceをslice2に代入
fmt.Println(slice2) //=>[Golang Ruby]

//ex6:スライスのゼロ値は nil
var slice3 []int
fmt.Println(slice3, len(slice), cap(slice)) //=> [] 0 0

if slice3 == nil {
fmt.Println("nil!") //=> nil!
//sliceの値がnilの場合にnil!を表示する。
}
}


Maps(連想配列)

Maps(連想配列)ポイント



  • Maps(連想配列)キー (key)というデータを使って要素を指定するデータ構造である。

  • 複数の宣言方法が存在する。(参考コード① ex1)

  • 初期値を指定しない場合、変数はnil(nil マップ)に初期化される。(参考コード① ex2)

  • 要素の挿入や削除が行える。(参考コード② ex3)

参考コード①

func main(){

//ex1複数の宣言方法が存在する

//①組み込み関数make()を利用して宣言
//make(map[キーの型]値の型, キャパシティの初期値)
//make(map[キーの型]値の型)

map1 := make(map[string]string)
map1["Name"] = "Mike"
map1["Gender"] = "Male"

//②初期値を指定して宣言
//var 変数名 map[key]value = map[key]value{key1: value1, key2: value2, ..., keyN: valueN}
map2 := map[string]int{"Age":25,"UserId":2}

//ex2初期値を指定しない場合、変数はnil(nil マップ)に初期化される
var map3 map[string]string

fmt.Println(map1, map2, map3) //=>map[Gender:Male Name:Mike] map[Age:25 UserId:2] map[]
}

参考コード②

func main(){

//ex3:要素の挿入や削除が行える

//連想配列の作成
var map_ex = map[int]string{1:"Go",2:"Ruby"}
fmt.Println(map_ex) //=> map[2:Ruby 1:Go]

//要素の挿入や更新
map_ex[3] = "Javascript"
fmt.Println(map_ex) //=> map[1:Go 2:Ruby 3:Javascript]

//要素の取得
fmt.Println(map_ex[2]) //=> Ruby

//要素の削除
delete(map_ex,3)
fmt.Println(map_ex) //=> map[2:Ruby 1:Go]
}


Range(forrange節)

Rangeポイント


  • スライスやマップに使用すると反復毎に2つの変数を返す。(ex1)

  • スライスの場合、1つ目の変数は インデックス(index)で、2つ目は要素(value)である。(ex2)

  • マップの場合、1つ目の変数はキー(key)で、2つ目の変数はバリュー(value)である。(ex3)

  • インデックスや値は、_へ代入することで省略することが可能。(ex4)

参考コード

//スライスとマップの作成 

var slice1 = []string{"Golang", "Ruby"}
var map1 = map[string]string{"Lang1":"Golang", "Lang2":"Ruby"}

func main(){
//ex1:スライスやマップに使用すると反復毎に2つの変数を返す。
//ex2:スライスの場合、1つ目の変数は `インデックス(index)`で、`2つ目は要素(value)`である。
for index, value := range slice1{
fmt.Println(index,value)
//=> 0 Golang
//=> 1 Ruby
}

//ex3:マップの場合、1つ目の変数は`キー(key)`で、2つ目の変数は`バリュー(value)`である。
for key, value := range map1{
fmt.Println(key, value)
//=> Lang1 Golang
//=> Lang2 Ruby
}

//ex4:インデックスや値は、 _ へ代入することで省略することが可能。

for _,value := range map1{
fmt.Println(value)
//=> Golang
//=> Ruby
}
}


Interface(インターフェース)

Interface(インターフェース)ポイント


  • 任意の型が「どのようなメソッドを実装するべきか」を規定するためのもの。

  • インターフェースの定義の内容は、単なるメソッドリストである。

  • オブジェクト指向言語でいうところのポリモーフィズムと同様の機能を実現可能。

参考コード①ではPersonPerson2という構造体があり、各構造体にintro()メソッドが定義されており、各構造体がそれぞれintro()を実行するためのIntroForPerson()IntroForPerson2()と呼ばれるメソッドを持っている。

参考コード①

type Person struct {} //Person構造体

type Person2 struct {} //Person2構造体

//Person構造体のメソッドintro()
func (p Person) intro() string{
return "Hello World"
}

//Person2構造体のメソッドintro()
func (p Person2) intro() string{
return "Hello World"
}

//Person構造体のメソッドintro()を実行するメソッド
func IntroForPerson(arg *Person){
fmt.Println(arg.intro())
}

//Person2構造体のメソッドintro()を実行するメソッド
func IntroForPerson2(arg *Person2){
fmt.Println(arg.intro())
}

func main(){
bob := new(Person)
mike := new(Person2)

IntroForPerson(bob) //=> Hello World
IntroForPerson2(mike) //=> Hello World
}

現状ではIntroForPersonIntroForPerson2の同じ動きをするメソッドが存在しており冗長である。

そこで、参考コード②の様に重複するメソッドをInterface(インターフェース)に括り出す事ができる。

参考コード②

type Person struct {} //Person構造体

type Person2 struct {} //Person2構造体

type People interface{
intro()
}

func IntroForPerson(arg People) {
arg.intro();
}

//Person構造体のメソッドintro()
func (p *Person) intro() {
fmt.Println("Hello World")
}

//Person2構造体のメソッドintro()
func (p *Person2) intro() {
fmt.Println("Hello World")
}

func main(){
bob := new(Person)
mike := new(Person2)

IntroForPerson(bob) //=> Hello World
IntroForPerson(mike) //=> Hello World
}


goroutine(ゴルーチン)

goroutine(ゴルーチン)ポイント


  • Go言語のプログラムで並行に実行されるもののこと。

  • 関数(またはメソッド)の呼び出しの前にgoを付けると、異なるgoroutineで関数を実行することが可能。(ex1)


  • runtime.NumGoroutine()を使用する事で現在起動しているgoroutine(ゴルーチン)の数を知ることが可能。(ex2)

参考コード

package main 

import(
"fmt"
"log"
"runtime"
)
func main(){
fmt.Println("Hello World")
//ex1:関数(またはメソッド)の呼び出しの前にgoを付けると、異なるgoroutineで関数を実行することが可能。
go hello()
go good_morning()
//ex2:runtime.NumGoroutine()を使用する事で現在起動しているgoroutine(ゴルーチン)の数を知ることが可能。
log.Println(runtime.NumGoroutine())
//=> Hello World
//=> 2009/11/10 23:00:00 3
//3がgoroutineの数。
//ここではmain, hello, good_morningの3つを指す。
}

func hello(){
fmt.Println("Hello")
}

func good_morning(){
fmt.Println("Good Morning")
}


channel(チャネル)

channel(チャネル)ポイント


  • 組み込み関数make()を使用する事で生成可能。(参考コード① ex1)

  • チャネルオペレータの<-を用いる事で値の送受信が可能。(参考コード① ex2)

  • 送信がchannel<-valueで受信が<-channel(参考コード① ex2+)


  • channel(チャネル)は値の交換および同期という通信機能を兼ね備えており、ゴルーチンが予期しない状態とならないことを保証する。(参考コード② ex3)

参考コード①

func main(){

//ex1:組み込み関数`make()`を使用する事で生成可能。
ch := make(chan string)

//ex2:<-を用いる事で値の送受信が可能。
//ex2+:作成したチャネルchに値を送信。
go func(){ ch <- "str"}()

//ex2+:チャネルchから値を受信
msg := <- ch
fmt.Println(msg) //=>str
}

参考コード②

func main(){

//ex3:ゴルーチンが予期しない状態とならないことを保証する。

ch := make(chan bool) //bool型のチャネルchを作成

// ゴルーチンとして以下の関数を起動。
//完了時にchannelの型であるboolの値を送信する事で、チャネルへ通知。
go func(){
fmt.Println("Hello")
ch <- true // 通知を送信。値は何でも良い(boolの型であれば)
}()

<- ch //=>Hello
// channelの型であるboolの値を受け取るまでの完了待ち。送られてきた値は破棄
}