Go言語 文法まとめ
Package
- Goのプログラムは、パッケージ(
package
)で構成されます - プログラムは
main
パッケージから開始されます - 規約で、パッケージ名はインポートパスの最後の要素と同じ名前になります
- ex)
math/rand
→rand
パッケージ
- ex)
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("My favorite number is", rand.Intn(10))
}
imports
// factoredインポートステートメント
import (
"fmt",
"math",
"パッケージ名"
)
// 複数のインポートステートメント
import "fmt"
import "math"
import "パッケージ名"
Exported names
package main
import (
"fmt"
"math"
)
func main() {
// 最初の文字が大文字で始まる名前は、外部のパッケージから参照できるエクスポート(公開)された名前( exported name )
fmt.Println(math.Pi)
// 小文字ではじまる pi や hoge などはエクスポートされていない名前です。
// fmt.Println(math.pi)
}
関数
// 変数名の後ろに型名を書く
func 関数名(変数名1 型名, ...) 戻り値の型名 {
// ...
return 戻り値
}
// 関数の2つ以上の引数が同じ型である場合には、最後の型を残して省略して記述できます。
func 関数名(変数名1, 変数名2 型名) {
// ...
return 戻り値
}
// 複数の戻り値を返す
func 関数名(変数名 型名, ...) (型名, 型名) {
// ...
return 戻り値1, 戻り値2
}
ex) 関数
package main
import "fmt"
func add1(x int, y int) int {
return x + y
}
func add2(x, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
ex) 複数の戻り値を返す
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
Named return values
Goでの戻り値となる変数に名前をつける(named return value
)ことができます。戻り値に名前をつけると、関数の最初で定義した変数名として扱われます。
func 関数名(引数) (x 型名, y 型名) {
x = ...
y = ...
return
}
ex)
package main
import "fmt"
// xとyがnamed return valueになる
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
func main() {
fmt.Println(split(17))
}
変数
var
ステートメントは変数(variable
)を宣言します。 関数の引数リストと同様に、複数の変数の最後に型を書くことで、変数のリストを宣言できます。
-
var
ステートメントはパッケージ、または、関数で利用できます
// 変数宣言
// var 変数名(, 変数名2, 変数名3, ...) 型名
var i int
var c, java, java bool
// 初期化宣言. 初期化子が与えられている場合、型を省略できます
// var 変数名(, 変数名2, 変数名3, ...) = 値(, 値2, 値3, ...)
var i, j int = 1, 2
var c, python, java = true, false, "no!"
// 関数の中では、`var`宣言の代わりに、短い`:=`の代入文を使い、暗黙的な型宣言ができます。
// なお、関数の外では、キーワードではじまる宣言(`var`, `func`, など)が必要で、`:=`での暗黙的な宣言は利用できません。
func main() {
k := 3
}
Go言語の基本型
型 | Go言語の基本型 | デフォルト値 |
---|---|---|
真偽 | bool |
false |
文字列 | string |
"" |
整数 |
int , int8 , int16 , int32 , int64 , uint , uint8 , uint16 , uint32 , uint64 , uintptr
|
0 |
バイト |
byte (uint8 の別名) |
? |
? |
rune (int32 の別名. Unicode のコードポイントを表す.) |
? |
浮動小数 |
float32 , float64
|
0 |
? |
complex64 , complex128
|
? |
型変換
変数v
、型T
があった場合、T(v)
は、変数v
をT
型へ変換します。
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
// シンプルな記述
i := 42
f := float64(i)
u := uint(f)
明示的な型を指定せずに変数を宣言する場合(:=
やvar =
のいずれか)、変数の型は右側の変数から型推論されます
var i int
j := i // j is an int
右側に型を指定しない数値である場合、左側の新しい変数は右側の定数の精度に基いてint
, float64
, complex128
の型になります
i := 42 // int
f := 3.142 // float64
g := 0.867 + 0.5i // complex128
定数
定数は、const
キーワードを使って変数と同じように宣言します。
定数は、文字(character)、文字列(string)、boolean、数値(numeric)のみで使えます。
なお、定数は:=
を使って宣言できません。
package main
import "fmt"
const Pi = 3.14
func main() {
const World = "世界"
fmt.Println("Hello", World)
fmt.Println("Happy", Pi, "Day")
const Truth = true
fmt.Println("Go rules?", Truth)
}
For文
for 初期化; 条件式; 後処理 {
// ループは、条件式の評価がfalseとなった場合にイテレーションを停止します。
}
for ; 条件式; {
// 初期化と後処理ステートメントの記述は任意
}
for 条件式 {
// ;を省略することもできる
}
for {
// 無限ループ
}
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
// 初期化と後処理ステートメントの記述は任意
sum := 1
for ; sum < 1000; {
sum += sum
}
sum := 1
for sum < 1000 {
sum += sum
}
If文
if 条件式 {
} else {
}
// ifステートメントは、 forのように、条件の前に、評価するための簡単なステートメントを書くことができます。
// ここで宣言された変数は、 if のスコープ内だけで有効です。
if ステートメント; 条件式 {
} else {}
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
}
return lim
}
Switch文
- 他言語のSwitchと似ている
- 上から下へcaseを評価する
- しかし、Goでは選択された
case
だけを実行してそれに続く全てのcase
は実行されない(自動的にbreak
される) - もう一つの重要な違いは Goの
switch
のcase
は定数である必要はなく、 関係する値は整数である必要はないということです
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
}
条件のないswitchは、switch true
と書くことと同じです。
このswitchの構造は、長くなりがちな "if-then-else" のつながりをシンプルに表現できます。
package main
import (
"fmt"
"time"
)
func main() {
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.")
}
}
Defer
defer
ステートメントは、defer
へ渡した関数の実行を、呼び出し元の関数の終わり(return
する)まで遅延させるものです。
defer
へ渡した関数の引数は、すぐに評価されますが、その関数自体は呼び出し元の関数がreturnするまで実行されません。
package main
import "fmt"
func main() {
defer fmt.Println("world") // main関数が終了してから実行される
defer fmt.Println("!!!")
fmt.Println("hello")
}
// hello
// world
// !!!
ポインタ
Goはポインタを扱います。ポインタは値のメモリアドレスを指します。
// 変数Tのポインタは、*T型で、デフォルト値(ゼロ値)はnilです。
var p *int
// & オペレータは、そのオペランドへのポインタを引き出します。
i := 42
p = &i
// * オペレータは、ポインタの指す先の変数を示します。
fmt.Println(*p) // ポインタpを通してiから値を読みだす
*p = 21 // ポインタpを通してiへ値を代入する
Structs(構造体)
Goにはオブジェクト指向言語におけるclassというものは存在しません。
似た役割として関連する変数をひとまとめにする struct(構造体) が使用されます。
構造体の定義
type 構造体名 struct {
フィールド名1 型名
フィールド名2 型名
// ...
}
ex)
type Vertex struct {
X, Y int
name string
}
初期化方法
// ①初期化後にフィールドに値を代入
var v1 Vertex // もしくは var v1 Vertex{}
v1.X = 1
v1.Y = 2
v1.name = "ほげ"
// ②順番にフィールドの値を渡す方法
v1 := Vertex{1, 2, "ほげ"}
// ③フィールド名を : で指定する方法
v1 := Vertex{name: "ほげ"}
フィールドへのアクセス
// structのフィールドは、ドット( . )を用いてアクセスします。
v := Vertex{1, 2}
v.X = 4
// ポインタを通してアクセス
v := Vertex{1, 2}
p := &v
p.X = 1e9
fmt.Println(v)
メソッドの定義
Goにはクラスと言う概念は存在しませんが、構造体内にメソッドmethod
を定義できます。
func (変数名 構造体名) メソッド名(引数) 戻り値の型名 {
// 処理...
}
例)
// 構造体
type Person struct {
first_name string
age int
}
// メソッド
func (p Person) intro(greetings string) string {
return greetings + " I am " + p.first_name
}
func main() {
bob := Person{"Bob", 30}
fmt.Println(bob.intro("Hello")) //=> Hello I am Bob
}
任意の型(例えば、int
, float
, ...)にもメソッドを宣言できる。
ex) Abs
メソッドを持つ、数値型のMyFloat
型です。
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func main() {
f := MyFloat(-math.Pi)
fmt.Println(f.Abs())
// => 3.141592653589793
}
更新系メソッドの注意点
フィールドの値を更新する際は、ポイントレシーバを使うように
- メソッドがレシーバが指す先の変数を変更するため
- メソッドの呼び出し毎に変数のコピーを避けるため
type Person struct {
first_name string
age int
}
func (p *Person) changeAge(newAge int) {
p.age = newAge
}
func main() {
person := Person{"taiyo", 24}
person.changeAge(25)
fmt.Println(person) // => {taiyo 25}
}
構造体の埋め込み
Goはオブジェクト指向言語の様なクラスを持ちません。ですのでクラスの継承も存在しません。そこで継承に似た機能として構造体の埋め込みがあります。
package main
import "fmt"
type Person struct {
first_name string
}
func (a Person) name() string{ //Personのメソッド
return a.first_name
}
type User struct {
Person
}
func (a User) name() string { //Userのメソッド
return a.first_name
}
func main(){
bob := Person{"Bob"}
mike := User{}
mike.first_name = "Mike"
fmt.Println(bob.name()) //=> Bob
fmt.Println(mike.name()) //=> Mike
}
Arrays
- 固定長の配列
- 宣言した後に、配列のサイズを変えることはできません
宣言例
[2]string
[3]bool{true, true, false}
[]bool{true, true, false}
// [n]T 型は、型 T の n 個の変数の配列( array )を表します。
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)
primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)
Slices
- 配列への参照のようなもの
- スライスはどんなデータも格納しておらず、単に元の配列の部分列を指し示しています
- スライスの要素を変更すると、その元となる配列の対応する要素が変更されます
- 同じ元となる配列を共有している他のスライスは、それらの変更が反映されます
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4]
fmt.Println(s)
names := [4]string{"John", "Paul", "George", "Ringo",}
fmt.Println(names)
a := names[0:2]
b := names[1:3]
fmt.Println(a, b)
b[0] = "XXX"
fmt.Println(a, b)
fmt.Println(names)
// => [John Paul George Ringo]
// => [John Paul] [Paul George]
// => [John XXX] [XXX George]
// => [John XXX George Ringo]
スライスするときは、それらの既定値を代わりに使用することで上限または下限を省略することができます。
// 以下の配列において
var a [10]int
// これらのスライス式は等価です
a[0:10]
a[:10]
a[0:]
a[:]
スライスの長さと元の配列の長さ
s := []int{2, 3, 5, 7, 11, 13}
// len(s) => 6
// cap(s) => 6
スライスのデフォルト値(ゼロ値)はnil
です。
var s []int
s == nil // => true
make関数
make([]型名, len, cap)
func main() {
a := make([]int,5, 5)
fmt.Println(a) //=> [0 0 0 0 0]
}
append関数
append(スライス, 追加する値(, 追加する値2, ...))
ex)
var s []int
s = append(s, 0, 1, 2)
// s => [0 1 2]
range
forループに利用するrange
は、スライスや、マップ(map
)をひとつずつ反復処理するために使います。
for i, v := range スライスorマップ {
i // => インデックス番号
v // => 要素
}
// インデックスや値は、 "_"へ代入することで捨てることができます。
for _, v := range スライスorマップ {
// ...
}
// もしインデックスだけが必要なら、", value"を省略する
for i := range スライスorマップ {
i // => インデックス番号
}
Maps(連想配列, 辞書)
Mapsの宣言
// 組み込み関数make()を利用して宣言. (デフォルト値(ゼロ値)はnill)
make(map[キーの型]値の型, キャパシティの初期値)
make(map[キーの型]値の型)// キャパシティの初期値は、省略も可能
// 初期値を指定して宣言
var 変数名 map[キーの型]値の型 = map[キーの型]値の型{key1: value1, key2: value2, ..., keyN: valueN}
Mapsの操作
挿入や更新
m[key] = value
要素の取得
elem = m[key]
要素の削除
delete(m, キー)
キーに対する要素が存在するかの確認
elem, ok := m[key]
// もし、 m に key があれば、変数 ok は true となり、存在しなければ、 ok は false となります。
// なお、mapに key が存在しない場合、 elem はmapの要素の型のゼロ値となります。
Function values(lambda?)
関数も変数です。他の変数のように関数を渡すことができます。
- 関数値( function value )は、関数の引数に取ることもできますし、戻り値としても利用できます。
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(5, 12)) // => 13
}
Interfaces
- Interface(インターフェース)とは、メソッドの型だけを定義した型のことです。
- Interface(インターフェース)を利用することで、オブジェクト指向言語でいうところのポリモーフィズムと同様の機能を実現できます。
定義
type インタフェース名 interface {
インタフェースのメソッド名1(引数名 型名, ...) 返り値の型
インタフェースのメソッド名1(引数名 型名, ...) (戻り値の型名, 戻り値の型名, ...)
// ...
}
type 構造体 struct {
// 定義...
}
func (変数 構造体) インタフェースのメソッド名1(引数名 型名, ...) 返り値の型 {
// 処理...
}
func (変数 構造体) インタフェースのメソッド名1(引数名 型名, ...) (戻り値の型名, 戻り値の型名, ...) {
// 処理...
}
空インタフェース
- 全ての型と互換性を持っている
interface{}型
(空インターフェース)というものが存在しています
定義
interface{}
以下の例の様に interface{}(空インターフェース) で宣言した変数にはどんな型の値でも代入可能です
var obj interface{}
obj = 0123 // int
obj = "String" // string
obj = []string{"Python", "Golang", "Ruby"} // slice
obj = func greetings(_ string) string { return "Hello World" } // function
型アサーション
全ての型と互換性を持っているinterface{}
ですが、interface{}型
の引数で受け渡された値は、元の型の情報が欠落しています。
var i interface{} = 値
// インターフェースの値 i が具体的な型 T を保持し、基になる T の値を変数 t に代入することを主張します。
t := i.(T)
// i が T を保持していれば、 t は基になる値になり、 ok は真(true)になります。
t, ok := i.(T)
var i interface{} = "hello"
s := i.(string)
// i が T を保持していれば、 t は基になる値になり、 ok は真(true)になります。
s, ok := i.(string)
f, ok := i.(float64)
型switch
データの型判定はswitch
文でも行うことができます。Go言語では、これを型switchといいます。
以下の様に型switch
を書く事ができます。
switch v := x.(type) {
case 型名1: ... // v は型名1の値になる
case 型名2: ... // v は型名2の値になる
...
default: ...
}
ex
func do(i interface{}) {
switch variable := i.(type) {
case int:
fmt.Println(variable)
case string:
fmt.Println(variable)
default:
fmt.Println("Default")
}
}
func main() {
do(23) //=> 23
do("hello") //=> hello
do(true) //=> Default
}
Stringers
もっともよく使われているinterfaceの一つにfmt
パッケージに定義されているStringer
があります
type Stringer interface {
String() string
}
-
Stringer
インタフェースは、string
として表現することができる型です -
fmt
パッケージ(と、多くのパッケージ)では、変数を文字列で出力するためにこのインタフェースがあることを確認します
Error
Goのプログラムは、エラーの状態をerror
値で表現します。
error
型はfmt.Stringer
に似た組み込みのインタフェースです
type error interface {
Error() string
}
よく、関数はerror
変数を返します。そして、呼び出し元はエラーがnil
かどうかを確認することでエラーをハンドル(取り扱い)します。
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)
Discussion