初めに
Goの公式チュートリアルを使用して勉強した内容をアウトプットするため記事を投稿する。
パッケージ
記述の初め方
Goでは、mainパッケージから開始する。
package main
パッケージのインポート
記述方法1
import{
"パッケージ1"
"パッケージ2"
}
記述方法2
import "パッケージ1"
import "パッケージ2"
記述方法1のほうがコンパクトで分かりやすい。
エクスポート
最初の文字が大文字だと、エクスポートされて外部パッケージからアクセスすることができる。
小文字だと、外部パッケージからアクセスすることができない。
基本的な書き方
mainパッケージを導入し、main関数内に処理を記載する。
package main
func main() {
}
関数
基本的な書き方
func add(a int, b int) int {
return a + b
}
//関数呼び出し
func main() {
sum := add(3, 4)
fmt.Println(sum) // 結果: 7
}
複数の戻り値
func numbers() (int, int) {
return 5, 7
}
引数の型が同じ場合
func 関数名(引数, 引数 型) {
処理
}
// 例:int型の引数2つと、string型の引数1つの場合
func 関数名(引数, 引数 int, 引数 string) {
処理
}
戻り値に名前をつける(naked return)
定義した変数と、関数の戻り値の変数名が異なっていたり、returnがなければエラーになる。
また、関数のブロック内の変数でvarや := を使用して定義してもエラーになる。
func 関数名()(変数名1, 変数名2) {
変数名1 = "test1"
変数名2 = "test2"
return
}
関数は値である
Goの関数は値であるため、変数に格納することができる。
また、関数の引数に関数を用いることもできる。
例
関数を変数に代入
var hoge = func(){}
// 呼び出し
hoge()
関数を関数の引数に用いる
呼び出す関数名(引数の関数名)
var Puls = func(a, b int) int {
c := a + b
return c
}
func Puls2(fn func(int, int) int) int {
ret := fn(2, 2)
return ret
}
func main() {
ret := Puls(1, 1)
fmt.Println(ret)
ret = Puls2(Puls)
fmt.Println(ret)
}
出力結果
2
4
関数を戻り値にする
関数を戻り値にすることも可能である。
func 関数名() func() 戻り値である関数の戻り値の型{}
func Test() func() int {
return func() int {
return 1
}
}
func main() {
// retにTest関数が格納される
ret := Test()
// Test関数の中の戻り値の関数を呼び出す
fmt.Println(ret())
}
出力結果
1
Closure
Closure関数に定義している変数a
の範囲は、increment1
とincrement2
で異なることがわかる。
func Closure() func() int {
var a int
return func() int {
a++
fmt.Println(a)
return a
}
}
func main() {
increment1 := Closure()
increment2 := Closure()
increment1()
increment1()
increment2()
increment2()
}
Closure関数に定義している変数a
の範囲は、increment1
とincrement2
で異なることがわかる。
出力結果
1
2
1
2
また、Closure関数で定義した変数をmain関数ないで使用するとエラーになる。
func Closure() func() int {
var a int
return func() int {
a++
fmt.Println(a)
return a
}
}
func main() {
increment1 := Closure()
increment1()
fmt.Println(a)
}
出力結果
undefined: a
上記からわかるように、クロージャーは変数のスコープを限定することができる。
変数
varを用いた宣言
型はなくとも宣言は可能で、Println関数で型を付与せずに宣言をした変数を出力するとfalseが出力される。
var test
初期値を与えずに変数を宣言し、型を付与するとゼロ値が与えられる。
bool型はfalse、int型は0、string型は""がゼロ値。
var b bool
var i int
var s string
fmt.Println(b, i, s)
// 出力結果
false 0
初期化子も同時に付与でき、その場合、変数の型は初期化子の型になる。
型を付与せずに代入した場合は、型推論が行われる。
// 型あり
var i, j int = 1, 2
// 型省略(型推論)
var b, f, s = true, false, "st"
// 初期化子と別の型の値を代入してみる
var b = "error"
// エラー内容
./prog.go:9:6: b redeclared in this block
./prog.go:8:6: other declaration of b
再代入
型が同じであれば再代入が可能。
var x, y int = 3, 4
var s, t = "s", "t"
x, y = 5, 5
s, t = "a", "i"
:=を使用した宣言
関数の中のみで:=を使用して代入できる。
型は初期化子の型となり、自分で記載することはできないようだが、変数の代入を行う場合は、シンプルなGoとの相性もありこちらを使用した方が見やすいように思う。
関数の外で使用すると構文エラーになる。
func main() {
i := 1
s, b := "st", true
}
再代入
varで宣言した変数同様に型が同じであれば再代入可能。
func main() {
i := 1
s, b := "st", true
i = 2
s, b = "rest", false
fmt.Println(i, s, b)
}
// 出力結果
2 rest false
型
型の一覧は以下を参照。
https://go-tour-jp.appspot.com/basics/11
For文
基本形
・中括弧が不要。
・波括弧が必須。
・「初期化ステートメント」「後処理ステートメント」は必須ではない。
// 基本形
// []は分かりやすく書いているだけなので実際のコードには不要
for [初期化ステートメント]; [条件式]; [後処理ステートメント] {}
// 「初期化ステートメント」「後処理ステートメント」を使用しない場合
// セミコロンは省略可能
for ; [条件式]; {}
無限ループ
for{}
if文
基本形
・中括弧が不要。
・波括弧が必須。
if [条件式] {}
short statement
・条件式の前に評価するための式を記載する。
・short statementの変数はif文のスコープ(else ブロック内含む)内のみで使用可能である。
・この機能があることにより、if文でのみ使用する変数のスコープを、if文のブロック内に設定することができる。
if [short statement] ; [条件式] {}
例
func main() {
if i := 1; i == 1 {
fmt.Println("Go")
}
}
// 出力結果
Go
指定した時間ループする
指定した時間ループするコードを書く機会があったので備忘録として記載する。
now := time.Now()
// 16の箇所はループさせたい秒数を記載する
for (16 * time.Second) > time.Since(now) {}
Switch文
基本形
Goのswitch文は、caseの条件が当てはまれば自動的にbreakステートメントが付与される。
また、caseは定数である必要がない。
caseの条件が全て満たされなかったらdefaultが処理される。
defaultは省略可。
また、caseの値は複数指定できる。
switch (条件){
case 値:
処理
case 値, 値:
処理
default:
処理
}
例
// caseの条件が満たされると、次のcaseが評価されずにbreakが実行されていることがわかる
func main() {
i := 1
switch i{
case 1:
fmt.Println("1つめ")
i++
fmt.Println(i)
case 2:
fmt.Println("2つめ")
default:
fmt.Println("3つめ")
}
}
// 出力結果
1つめ
2
fallthrough
fallthroughを使用すると次のcase文の処理が実行される。
fallthroughを実行した次のcaseの評価が満たされていなくとも実行がされるようである。
例
func main() {
i := 1
switch i {
case 1:
fmt.Println("1つめ")
fallthrough
case 2:
fmt.Println("2つめ")
fallthrough
default:
fmt.Println("3つめ")
}
}
// 出力結果
1つめ
2つめ
3つめ
boolean
switchの後に何も記載がない場合はcaseの評価がtrueの場合に処理が実行される。
また、switchの後にfalseを付与するとcaseの評価がfalseの場合に処理が実行される。
場合によってはif文よりもシンプルに条件分岐が可能である。
// caseの評価がtrueの場合に処理を実行
func main() {
i := 1
switch {
case 1 == i:
fmt.Println("1つめ")
case 2 == i:
fmt.Println("2つめ")
default:
fmt.Println("3つめ")
}
}
// 出力結果
1つめ
// caseの評価がfalseの場合に処理を実行
func main() {
i := 1
switch false{
case 1 == i:
fmt.Println("1つめ")
case 2 == i:
fmt.Println("2つめ")
default:
fmt.Println("3つめ")
}
}
// 出力結果
2つめ
Defer
基本形
deferに渡した関数は、呼び出し元の関数の処理が終了してから実行される。
渡した関数の引数はすぐに評価される。
func main() {
defer fmt.Println("2つ目")
fmt.Println("1つ目")
}
// 出力結果
1つ目
2つ目
複数deferの使用
まだ勉強中であるが、以下のページを参照すると分かりやすい。
A Tour of Go Stacking defers