Go/変数
変数編集
変数宣言編集
変数宣言では、1つまたは複数の変数を作成し、対応する識別子を結合し、それぞれに型と初期値を与えます[1]。
- 構文
VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) ; VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) ;
ゼロ値編集
Goで変数を使うためには、宣言が必要です。
- 宣言とゼロ値
package main import "fmt" func main() { var n int fmt.Printf("var n int => 値:%v 型:%T\n", n, n) }
- 実行結果
var n int => 値:0 型:int
- 解説
var n int
- int型の変数 n を宣言しています。
fmt.Printf("var n int => 値:%v 型:%T\n", n, n)
- fmt.Printf はC言語のprintf関数に影響を受けた関数で、fmt パッケージに属し書式化付きで与えられた引数を表示します。
- ここで使った書式は %v と %T の2つの動詞で、%v は引数の値を引数の値にあった型で表示します。%T は引数の型を表示します。
- ゼロ値
- 大事なのは値が 0 であることで、これは未定義な値がたまたま 0 だったのではなく言語仕様に従った結果だということです。
- このようにある型の変数が初期化さなかったときに初期値とされる値をゼロ値(Zero value)と呼びます。
- 様々な型のゼロ値
package main import "fmt" func main() { var i int // 整数型 fmt.Printf("var i int => 値:%v 型:%T\n", i, i) var f float64 // 浮動小数点数型 fmt.Printf("var f float64 => 値:%v 型:%T\n", f, f) var b bool // 真理値型 fmt.Printf("var b bool => 値:%v 型:%T\n", b, b) var s string // 文字列型 fmt.Printf("var s string => 値:%v 型:%T\n", s, s) var a [5]int // 5要素の整数配列型 fmt.Printf("var a [5]int => 値:%v 型:%T\n", a, a) var e []int // 整数スライス型 fmt.Printf("var e []int => 値:%v 型:%T\n", e, e) var r rune // Rune型(int32型のalias) fmt.Printf("var r rune => 値:%v 型:%T\n", r, r) var t struct { // 文字列型のフィールド Name と int型のフィールド Age を持つ無名の構造体 Name string Age int } fmt.Printf("var struct { Name string; Age int } => 値:%v 型:%T\n", t, t) }
- 実行結果
var i int => 値:0 型:int var f float64 => 値:0 型:float64 var b bool => 値:false 型:bool var s string => 値: 型:string var a [5]int => 値:[0 0 0 0 0] 型:[5]int var e []int => 値:[] 型:[]int var r rune => 値:0 型:int32 var struct { Name string; Age int } => 値:{ 0} 型:struct { Name string; Age int }
- 型の説明の前なので、多くの型とその書式は初見かもしれませんが、数値は 0 、文字列は ""、複合型は要素ごとに再帰的にゼロ値が与えられます。
初期化と型推論編集
Goでは、変数宣言で型を省略すると初期値から型を推論します。
- 初期化と型推論
package main import "fmt" func main() { var n = 5 fmt.Printf("var n = 5 => 値:%v 型:%T\n", n, n) }
- 実行結果
var n = 5 => 値:5 型:int
- 解説
var n = 5
で変数 n を初期値を 5 で宣言しています。 明示的に型を伴って宣言していませんが、初期値の 5 から int が型推定されます。
- fmt.Printf
fmt.Printf("var n = 5 => 値:%v 型:%T\n", n, n)
短い変数宣言編集
変数宣言にはもう1つの構文があります。
- 短い変数宣言
- 短い変数宣言は、以下の構文を使用します。
- 構文
ShortVarDecl = IdentifierList ":=" ExpressionList ;
これは、初期化式を持つが型を持たない通常の変数宣言の短縮形です。
- コード例
package main import "fmt" func main() { n := 5 fmt.Printf("var n = 5 => 値:%v 型:%T\n", n, n) }
- 実行結果
- (略)
- 解説
n := 5
同じ様に、int 型の変数 n を初期値を 5 で宣言しています。 var を伴った宣言と異なり、初期値は省略できません。
短い変数宣言は、if文、switch文やfor文に「文スコープな変数」を宣言する時に使われます。
複数の変数の一括宣言編集
- 構文
VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) ;
の後半 "(" { VarSpec ";" } ")"
は、宣言子の繰り返しを表しています。
- コード例
<syntaxhighlight lang="go"> package main import "fmt" var ( a = 100 b = "string" v = []struct { name string number int }{ {"abc", 1}, {"def", 2}, {"ghi", 3}, {"xyz", 9}, } f = 3.14 ) func main() { fmt.Printf("a: 値:%v 型:%T\n", a, a) fmt.Printf("b: 値:%v 型:%T\n", b, b) fmt.Printf("v: 値:%v 型:%T\n", v, v) fmt.Printf("f: 値:%v 型:%T\n", f, f) }
- 実行結果
a: 値:100 型:int b: 値:string 型:string v: 値:[{abc 1} {def 2} {ghi 3} {xyz 9}] 型:[]struct { name string; number int } f: 値:3.14 型:float64
a,bそれにfはある程度予想が着くと思います。 vは匿名構造体配列の複合リテラルです。 この様に fmt.Printf の %v ならびに %T の2つの動詞(C言語で言う型指定子)は、ユーザー定義を含め任意のオブジェクトの値と型を表示できるので便利です。
整数と浮動小数点数編集
上記の例のように、浮動小数点数リテラルの型は float64 です。この他に float32 という型もありますが、 float という型はありません。
整数型のビット精度編集
浮動小数点数型以外に整数型 int にも、ビット幅を伴った、int32
, int64
やint8
, int16
があります。
このような低精度の整数型にはニーズがないと、感じることがあるかもしれないが回帰型ニューラルネットワークなどの人工知能応用では精度は必ずしも必要なく、メモリーフットプリントの削減と計算時間の削減という意味で今後もニーズがあります。
関数スコープ編集
キーワード var を使って宣言した場合も、 簡略表記「:=」を使った場合にも関数の中で宣言された変数のスコープは関数のブロックです(関数の中のfor文やif文で宣言された場合は文スコープになります)。 また簡略表記「:=」はパッケージスコープの変数の宣言には使えません。
文字列型編集
文字列変数を使うには、初期化に文字リテラル("文字列" の形式)や文字列を返す関数を使い型推論させる方法と、型名 string を明示する方法があります。
他の言語での文字列型 |
|
キーワード編集
以下のキーワードは予約済みで、識別子として使用することはできません[2]。
- Goのキーワード一覧
break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var
int や float32 などの(プリミティブな)型名はキーワードには含まれず、予め宣言された識別子となります。
予め宣言された識別子編集
ユニバースブロック(universe block[3])では、以下の識別子が暗黙的に宣言されています[4]。
- 予め宣言された識別子
Types: any bool byte comparable complex64 complex128 error float32 float64 int int8 int16 int32 int64 rune string uint uint8 uint16 uint32 uint64 uintptr Constants: true false iota Zero value: nil Functions: append cap close complex copy delete imag len make new panic print println real recover
- Go1.18で予め宣言された識別子に型 any が追加されました。これはinterface{}のaliasです。
型変換編集
変換は,式の型を変換によって指定された型に変更します。変換は,ソースの中で文字通りに現れるかもしれませんし,式が現れる文脈によって暗示されるかもしれません。 明示的な変換は,T(x)という形式の式で,Tは型であり,xはT型に変換できる式である[5]。
- 構文
Conversion = Type "(" Expression [ "," ] ")" .
Goは静的型システムを導入しており、C言語より厳密に演算における型の一致性を求めます(IntegerとRealの足し算ですら明示的な型変換が必要なPascalと同等に厳格です)。 JavaScriptやRubyなどの動的な型システムを採用したシステムは、「数値型に文字列型を足す」、「文字列を1進める」などの異なる型同士の演算に於いて暗黙の型変換が行われます。 Goでは、どうしても異なる型を持った変数を足しあわせる処理などを場合、どちらかの変数の型に変換し、型を一致させる必要があります。
数値どうしの型変換編集
整数型で宣言した数を浮動小数型にしたり、float32型で宣言した数をfloat64型に変換するには、
- コード例
package main import "fmt" func main() { i := 8 fmt.Println("i = ", i) fmt.Println("i / 3 = ", i/3) f := float64(i) fmt.Println("f = ", f) fmt.Println("f / 3 = ", f/3) }
- 実行結果
i = 8 i / 3 = 2 f = 8 f / 3 = 2.6666666666666665
やや直感的ではありませんが、キーワード var を使った変数宣言で型名を明示した場合の初期値には暗黙の型変換は起こりません。
f := float64(i)
は
var f float64 = i
- コンパイルエラー
# command-line-arguments ./Main.go:10:9: cannot use i (type int) as type float64 in assignment
ではなく
var f float64 = float64(i)
とする必要があります。
ちなみに C言語の感覚で、
f := 1.0 * i
とすると f の型は(float64 ではなく)int です。
脚註編集
- ^ “Declarations_and scope ¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021) .
- ^ “Keywords ¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021) .
- ^ ユニバースブロックは、すべてのGoソーステキストを包含しています。
- ^ “Predeclared identifiers ¶”. The Go Programming Language Specification. The Go website. (March 10, 2022) .
- ^ “Conversions ¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021) .