Go/変数

出典: フリー教科書『ウィキブックス(Wikibooks)』
< 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, int64int8, int16 があります。

このような低精度の整数型にはニーズがないと、感じることがあるかもしれないが回帰型ニューラルネットワークなどの人工知能応用では精度は必ずしも必要なく、メモリーフットプリントの削減と計算時間の削減という意味で今後もニーズがあります。

関数スコープ[編集]

キーワード var を使って宣言した場合も、 簡略表記「:=」を使った場合にも関数の中で宣言された変数のスコープは関数のブロックです(関数の中のfor文やif文で宣言された場合は文スコープになります)。 また簡略表記「:=」はパッケージスコープの変数の宣言には使えません。

文字列型[編集]

文字列変数を使うには、初期化に文字リテラル("文字列" の形式)や文字列を返す関数を使い型推論させる方法と、型名 string を明示する方法があります。

他の言語での文字列型
C言語
string型が無く、'\0'で終端されたchar型の配列で表現していますが、文字列の連結などは標準ライブラリの関数となっており、結果を表すバッファのサイズなどへの責任はプログラマーが負い。このことが度々バッファオーバーフロー等を引き起こし脆弱性の原因となっています。
C++
string型はなく標準テンプレートクラスライブラリーの string クラスを使います。
Java
プリミティブには文字列はなく、String クラスを使います。
JavaScript
文字列プリミティブとStringオブジェクトがあります。
Fortran
ALLOCATABLEを宣言に併用することで可変長文字列に対応(Fortran 2003から)


キーワード[編集]

以下のキーワードは予約済みで、識別子として使用することはできません[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

intfloat32 などの(プリミティブな)型名はキーワードには含まれず、予め宣言された識別子となります。

予め宣言された識別子[編集]

ユニバースブロック(universe block[3])では、以下の識別子が暗黙的に宣言されています[4]

予め宣言された識別子
Types:
	bool byte 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

型変換[編集]

変換は,式の型を変換によって指定された型に変更します。変換は,ソースの中で文字通りに現れるかもしれませんし,式が現れる文脈によって暗示されるかもしれません。 明示的な変換は,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 = 
コンパイルエラー
# 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 です。


脚註[編集]

  1. ^ “Declarations_and scope ¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Declarations_and_scope. 
  2. ^ “Keywords ¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Keywords. 
  3. ^ ユニバースブロックは、すべてのGoソーステキストを包含しています。
  4. ^ “Predeclared identifiers ¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Predeclared_identifiers. 
  5. ^ “Conversions ¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Conversions.