Golang文法
プログラムのエントリーポイント
package main
import "fmt"
func main() {
fmt.Println("Yeah!!") // Yeah!!
}
変数
var i int
var x, y, z int
var (
x, y int
name string
)
var i int = 0
-
:=
を用いる場合、型は推定される
-
var
も省略してよい
// intと見なされる
i := 0
- 関数外部に宣言された変数は
パッケージ変数
になる
-
パッケージ変数
はパッケージ内がスコープになる(パッケージ内の関数からアクセス可能)
package main
import "fmt"
var msg string = "Yeah!!" // パッケージ変数
func main() {
fmt.Println(msg) // Yeah!!
}
- 関数内で宣言された変数は
ローカル変数
になる
-
ローカル変数
のスコープは関数内のみ
package main
import "fmt"
func main() {
var msg string = "Yeah!!" // ローカル変数
fmt.Println(msg) // Yeah!!
}
定数
-
const [定数名] = [定数]
またはconst [定数名] [型名] = [定数]
で宣言
- 慣例的に定数名は全て大文字で宣言される
const NAME = "yamada taro"
const NAME string = "yamada taro"
if文
- C言語系統のものと同じ
- 条件式を
()
で括らない流儀の模様
if x == 1 {
fmt.Println("x is 1")
}
var x int = 100
if x == 0 {
fmt.Println("x is 0")
} else if x > 100 {
fmt.Println("x is over 100")
} else {
fmt.Println("other")
}
- 条件式の前に代入文が書けたりする(簡易文付きif)
if [変数] := [値]; [判定式] { ... }
if x := f(); x == 1 {
fmt.Println("x is 1")
}
- ↑のような書き方の場合、
x
はif文のスコープ内でのみ有効になる(if文の外側のスコープを汚染しない)
- エラーハンドリングでよく使われるイディオム
if err := fn(); err != nil {
// error handling.
}
switch文
- Golangのswitch文には
break
は存在しない(自動的にbreak
される)
switch animal {
case "dog":
return "わおーん"
case "cat":
return "にゃーん"
default:
return "あ^〜"
}
- 逆に複数のcaseを跨ぎたい場合は
fallthrough
を使う
switch animal {
case "dog":
return "わおーん"
case "cat":
fallthrough
case "bobcat":
return "にゃーん"
default:
return "あ^〜"
}
switch number {
case 1, 2:
return "1 or 2"
case 3, 4:
return "3 or 4"
default:
return "0"
}
for文
- Golangには
for
文のみ(whle文は無い)
- 無限ループ
// 無限にYeah!!を出力
for {
fmt.Println("Yeah!!")
}
for i := 0; i < 100; i++ {
fmt.Println(i)
i++
}
for i := 0; i < 100; i++ {
if i % 2 == 1 {
continue
}
fmt.Println(i)
i++
}
for i := 0; i < 100; i++ {
fmt.Println(i)
i++
if i == 100 {
break
}
}
- iteratableなオブジェクトは
range
関数でループ
var numbers = [...]string{"One", "Two", "Three"}
for _, number := range numbers {
fmt.Println(number)
}
パッケージ
- Golangのソースファイルの1行目は必ずパッケージ宣言が必要
- JavaやPHPで言う名前空間的概念
-
package [パッケージ名]
で宣言
package mypackage
- パッケージに含まれる関数/変数/定数は先頭を大文字にすることでパッケージ外部に公開できる
- 逆に小文字で始まるものはパッケージ内でしか参照できない
package mypackage
const MAX = 99999 // 外部公開される定数
const name = "myname" // 非公開の定数
// 公開される関数
func PublicFn() int {
return 99999
}
// 非公開の関数
func privateFn() int {
return 99999
}
import "mypackage"
import (
"fmt"
"mypackage"
)
import my "mypackage" // myという別名を指定
- 深い階層のパッケージの場合、パスの最後の文字がパッケージ名になる
import "mypackage/sub/sub1"
func main() {
sub1.exec()
}
- ファイル名はパッケージに影響を与えない
- 同じディレクトリ内に異なるパッケージを配置することはできない
.
├── go.mod
├── go.sum
├── main.go
└── sub/
└─ sub1/
└─ sub2/
└─ mod1.go
└─ mod2.go
// mod1.go
package sub2
func Exec() string {
return "にゃーん"
}
// mod2.go
package sub2
func Exec2() string {
return "あ^〜"
}
// main.go
import "mypackage/sub1/sub2"
func main() {
sub2.Exec() // => にゃーん
sub2.Exec2() // => あ^〜
}
関数
func fn(x int, y int) int {
return x + y
}
func fn() {
fmt.Println("Yeah!!") // Yeah!!
}
- 戻り値を複数返すこともできる
- 複数の戻り値を返す場合は
()
で括る
func fn() (int, int) {
return 100, 1000
}
- 戻り値を受け取る側で
_
に代入すると変数の破棄を表す
func fn() (int, int) {
return 100, 1000
}
a, b = fn() //両方受け取る
_, b = fn() //1つ目の戻り値は破棄
a, _ = fn() //2つ目の戻り値は破棄
func fn(p ... int) {
for i, _ := range p {
fmt.Println(i)
}
}
fn(1, 2, 3, 4, 5, 6) // 123456
無名関数
- 以下のように無名関数を定義する
- 変数に代入した無名関数をコールする時は
()
を付けてやればOK
var fn = func() int {
return 1000
}
fmt.Println(fn()) // 1000
特殊関数
var fn = func() int {
return 1000
}
fmt.Println(fn()) // 1000
var fn = func() int {
return 1000
}
fmt.Println(fn()) // 1000
defer
- defer文を使用すると指定した関数を遅延実行できる
- defer文が指定された関数は親関数のスコープを抜けるタイミングで実行される
func f() {
defer fmt.Println("1")
fmt.Println("2")
}
f() // 2が出力された後、1が出力される
- defer文が複数存在する場合、行末から(下から)順番に実行される
func f() {
defer fmt.Println("1")
defer fmt.Println("2")
fmt.Println("3")
}
f() // 3,2,1の順に出力される
- リソースのクローズ処理としてイディオム的に使われる(try-finary的な)
- 以下のように関数を抜ける際に必ずクローズ処理が行なうことができる
func f() {
file, err := os.Open("test.txt")
if err != nil {
return
}
defer file.close()
fmt.Print(file)
}
f()
go
func main() {
go task1() // 並列実行
go task2() // 並列実行
fmt.Println("run main")
}
func task1() {
fmt.Println("run task1.")
}
func task2() {
fmt.Println("run task2.")
}
-
goroutine
で並行処理されている関数とのデータ共有はchannel
を使って行う
-
channel
にデータを送信する、channel
からデータを受信するという感じ
-
channel
は<-
演算子で記述する
-
channel
はmake
関数で作成する
ch := make(chan int)
package main
import "fmt"
func main() {
channel := make(chan int) // channelを作成
defer close(channel) // 使い終わったchannelはCloseする必要がある模様
go task(channel) // task関数の引数としてchannelを渡す
result := <- channel // channelから並行実行された関数のデータを取得
fmt.Println(result) // 9999999
}
func task(channel chan int) {
channel <- 9999999 // channelにデータを書き込み
}
struct
- C言語の
struct
の同類
- とりあえずクラスめいたものという理解でOK
type Person struct {
name string
age int
}
-
struct
のフィールドも先頭の大文字/小文字で公開/非公開を切り替える
type Person struct {
Name string // 公開フィールド
age int // 非公開フィールド
}
- 初期化は↓のように行う
- フィールドの定義順に初期化する方法と名前とデータをマッピングさせる方法がある
type Person struct {
name string
age int
}
// フィールドの定義順に値を入力
var p Person = Person{
"Jhon",
30
}
// フィールドの名前ベースで値を入力
var p Person = Person{
name: "Jhon",
age: 30
}
-
struct
にメソッドを持たせることが可能
- メソッドも関数名の先頭が大文字/小文字で公開/非公開を指定可能
-
(p *Person)
の部分をレシーバと呼ぶ模様
type Person struct {
name string
age int
}
func (p *Person) IsAdult() bool {
if p.age >= 18 {
return true
} else {
return false
}
}
// フィールドの名前ベースで値を入力
var p Person = Person{
name: "Jhon",
age: 30
}
result := p.IsAdult()
fmt.Println(result) // true
interface
- ある型が実装していなくてはならないメソッドのリストを定義する
type MyInterface interface {
MyMethod() string
}
-
interface
で定義されているメソッドを持っているstruct
はそのinterface
を実装したものとして扱われる
- ダックタイピング的な発想なのだろうか
type MyInterface interface {
MyMethod() string
}
type MyStruct1 struct {
value string
}
// MyMethodを持っているのでMyInterfaceを実装しているものと見なされる
func (s *MyStruct1) MyMethod() string {
return s.value
}
- 利用時は
interface
に定義した名前を宣言する
var mystruct MyInterface = &MyStruct1{"Yeah!!"} // ポインタを返す必要がある模様
result := mystruct.MyMethod()
fmt.Println(result) // Yeah!!
package main
import "fmt"
type MyInterface interface {
MyMethod() string
}
type MyStruct1 struct {
value string
}
func (s *MyStruct1) MyMethod() string {
return s.value
}
func main() {
var mystruct MyInterface = &MyStruct1{"Yeah!!"} // 変数の型はMyInterfaceだが、実体はMyStruct1
result := mystruct.MyMethod()
fmt.Println(result) // Yeah!!
}
基本型
- 論理値型(bool)
- 数値型(int)
- 符号付整数型
- 符号なし整数型
- uint8(byte)
- uint16
- uint32
- uint64
- 浮動小数点型
- 複素数型
- rune型(rune)
- 文字列型(string)
interface{}型
-
interface{}
型はあらゆる型と互換性があることになっている特殊な型
- Javaの
Object
型みたいな扱いだろうか
var x interface{} // xにはあらゆる型のデータを代入可能
-
interface{}
型に代入されたデータを別の型に変換
- 型アサーションと呼ぶ模様
var x interface{}
x = 100
val, ok := x.(int) // valにint型の変数、okにキャスト可能かどうかの真偽値が返る
- 型アサーションの機能を利用して型によって処理をスイッチさせることができる
- 型スイッチと呼ぶ模様
var x interface{} = 100
switch val := x.(type) {
case int:
fmt.Println("Int")
case string:
fmt.Println("String")
default:
fmt.Println("Other")
}
ポインタ
- 変数が格納されているメモリアドレス
- C言語でお馴染みのアレ
- ポインタ型変数は
*変数
で宣言(C言語と同じ)
-
&
で変数のアドレスを取得できる
package main
import "fmt"
func main() {
var val1 int = 1000
var val2 *int = &val1 // val2はポインタ型変数として宣言され、val1のアドレスを参照している
fmt.Println(val1) // => 1000
fmt.Println(val2) // => 実行時のメモリアドレス
}
参照渡しと値渡し
-
参照渡し
-- 関数に渡した値を変更すると元の変数も変わる
-
値渡し
-- 値をコピーして関数に渡されるので値を変更しても元の変数は変わらない
package main
import (
"fmt"
)
func main() {
a, b := 10, 10
called(a, &b) // aはそのまま、bはアドレス演算子をつけて呼び出す
fmt.Println("値渡し:", a)
fmt.Println("参照渡し:", b)
}
func subfn(a int, b *int) {
a = a + 1 // 変数をそのまま変更
*b = *b + 1 // 変数の中身を変更
}
値渡し:10
ポインタ渡し:11
配列
var myArray [3]int // 要素数が3でintの値を持つ配列を宣言
myArray[0] = 10 // インデックスで代入/参照が可能
var myArray [3]int = {10, 20, 30}
- 要素数に
...
を指定すると初期化時の要素数に合わせて領域を確保
var myArray [...]int = {10, 20, 30, 40, 50}
- Golangの配列は値でコピーされる(別の変数にコピーした場合、別の配列として確保される)
var myArray [...]int = {10, 20, 30, 40, 50}
myArray2 := myArray
myArray2[0] = 999 // myArray2の0番は999になるが、myArrayのデータは影響を受けない
Slice
var 変数名 []型
var 変数名 []型 = []型 {初期値1, ... 初期値n}
変数名 := [start:end]
package main
import "fmt"
func main() {
var slice1 []string
fmt.Println(slice1) // []
var slice2 []string = []string {"一", "二", "三"}
fmt.Println(slice2) // ["一", "二", "三"]
var slice3 := slice2[0:1]
fmt.Println(slice3) // ["一"]
}
- Sliceは参照渡しになる(参照型なので)
- Sliceを別の変数に代入し、その変数のSliceを変更した場合、コピー元のSliceも変化する
Map
- いわゆる連想配列やハッシュテーブル
-
map[キーの型]値の型
で定義
- 何種類か書き方があるが、実用上の違いは無さそうなので
m3
の書き方を覚えておけばよさそう
var m1 map[string] string = map[string]string {}
m2 := make(map[string]string)
m3 := map[string]string{}
- 初期値を設定する際は他の言語同様
key:value
で指定する
m := map[string] string {
"key1": "value1",
"key2": "value2",
"key3": "value3",
}
-
make
で作成した場合は初期値を設定できないのでキーを指定して代入する
m := make(map[string]string)
m["key1"] = "value1"
m["key2"] = "value2"
m["key3"] = "value3"
- アイテムの削除は
delete
delete(map変数, [キー])
delete(m, "key1")
_, ok := m["key100"]
if ok {
fmt.Println("exist.")
} else {
fmt.Println("not exist.")
}
m := map[string] string {
"key1": "value1",
"key2": "value2",
"key3": "value3",
}
for key, value := range m {
fmt.Printf("%s=%d\n", key, value)
}
type(型エイリアス)
-
type [定義する型の名前] [既存の型]
と書くことで定義済みの型から新しい型を定義できる(別名を付けれる?)
- 継承というわけではないが、使い勝手としては継承によるサブクラスぽさがある
type MyInt int
var i MyInt = 5
- マップやスライスを独自の型として扱いたい時などに便利そう
// string:interface{}で構成されるMapにConfigMapという別名を付けた
type ConfigMap map[string] interface{}
conf := ConfigMap {
"APP_NAME": "App1",
"DEBUG": true,
"VERSION": 1
}
conf["APP_NAME"] // App1
参考資料