はじめに
コンピュータプログラミング言語において、なぜ変数が必要なのか。これは古くからの問いである。私たちが LabEx の URL がlabex.ioであることを覚えているのと同じように、コンピュータプログラムも使用するためにいくつかのデータを覚える必要がある。
変数の目的は、1 つのデータを表すことである。このセクションでは、Go 言語において変数がどのように使用されるかを探っていく。
ポイント:
- 変数宣言
- 変数初期化
- 変数の使用
- 変数の生存期間
- 定数
コンピュータプログラミング言語において、なぜ変数が必要なのか。これは古くからの問いである。私たちが LabEx の URL がlabex.ioであることを覚えているのと同じように、コンピュータプログラムも使用するためにいくつかのデータを覚える必要がある。
変数の目的は、1 つのデータを表すことである。このセクションでは、Go 言語において変数がどのように使用されるかを探っていく。
ポイント:
変数とは何か。簡単に言えば、変数は 1 つの可変データを格納および保存するために使用されるコンテナである。
Go のようなコンパイル言語では、変数の型は固定されている。
これは、変数が 1 つの型のデータのみを保持できることを意味する。言い換えると、変数コンテナに格納されるアイテムは固定されている。
変数が果物を保持するために使用される場合、それは果物のみを保持する必要がある。コンテナが果物を保持した後は、クッキーを保持するために使用できなくなる。これは、変数に 2 つの異なる型のデータを割り当てることができないというコードに反映される。
名前の通り、変数の値は、その型が許す範囲を超えない限り変更することができる。
Go 言語は、変数に関して次のルールを持っている:
Go 言語では、一般的な方法で変数を宣言するために使用されるキーワードはvarである。
宣言の形式は:var identifier typeで、これは次の意味を持つ:
var variableName variableType.
Go における一般的な変数型は何か。
| キーワード | 説明 |
|---|---|
int |
整数。小学校の算数で学ぶ最も一般的なデータ型。 |
string |
文字列。二重引用符で囲まれた文字の列で、たとえば:"hello,world" |
bool |
ブール型。真偽を表し、2 つの可能な値:trueまたはfalseがある。 |
このセクションの焦点は変数型ではないため、最も一般的な 3 つの型のみを挙げている。
より多くの型については、次のコースで説明する。
では、aという名前の整数型の変数を宣言してみよう。
var a int
どのように覚えればよいか。頭の中で黙々と言ってみると:
`a`という名前の変数を定義し、それはint型である。
多くの伝統的なプログラミング言語とは異なり、Go で使用される変数型は変数名の後に置かれる。
このような変数の宣言方法は、コードを左から右に読みやすくし、C 言語の螺旋状の読み取りロジックを回避する。詳細については、公式ドキュメントを参照のこと。
良い変数名は、変数の意味を明確に示す必要がある。
変数を命名する際には、その表現力に注意を払い、略語を使わないようにする必要がある。
ここでは、変数命名の基本的な方法:キャメルケース命名規則について簡単に紹介する。
キャメルケース命名規則は、混合ケースの文字を使って変数を表す。最初の単語は小文字で、その後の各単語の先頭文字は大文字になる。
たとえば:currentDate。最初の単語currentは小文字で、2 番目の単語Dateは大文字で始まる。
このように、現在の日付を表す変数は簡単に理解できる。
この命名規則は、一般的に重要で頻繁に使用される変数に使用され、一時的な変数は重複を引き起こさない限り簡略化することができる。
では、3 つの変数を宣言してみましょう。
var a int // a という名前の整数型変数を宣言する
var b int // b という名前の整数型変数を宣言する
var c int // c という名前の整数型変数を宣言する
細心の注意を払っている学生の方は、a, b, cの 3 つの変数がすべてint型であることに気付かれたかもしれません。
その場合、変数名をカンマで結合して、コード量を削減することができます。
var a, b, c int // 3 つの変数 a, b, c を整数型として宣言する
しかし、3 つの変数が異なる型の場合どうなるでしょうか。
var a int // a という名前の整数型変数を宣言する
var b string // b という名前の文字列型変数を宣言する
var c bool // c という名前のブール型変数を宣言する
パッケージをインポートする際に似たような状況に遭遇したことがあるかもしれません。たくさんの異なる名前のパッケージを一緒にインポートする必要があるので、似たような書き方を使うことができます。
var (
a int
b string
c bool
)
このようなパッケージをインポートする際のような宣言の仕方は、一般的にグローバル変数を定義する際に使われます。
Go では、変数が宣言されるときにすべての変数に初期値が与えられます。変数の初期値が何であるかを調べてみましょう!
~/projectディレクトリにvarExercise.goという名前のファイルを作成します。
touch ~/project/varExercise.go
次のコードをファイルに書き込みます。
package main
import "fmt"
func main() {
var a int
var b string
var c bool
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
}
自分で実行してみて、以下の表と一致するかどうかを確認してみてください。
go run varExercise.go
初期値の型は以下のとおりです。
| キーワード | 説明 | 初期値 |
|---|---|---|
int |
整数 | 0 |
string |
文字列 | "" |
bool |
ブール型 | false |
変数の型は初期値によって決定できるので、デフォルト値や既に宣言された変数を変更できるでしょうか。
var a int = 1
var b string = "labex"
var c bool = true
a = 233
b = "labex"
c = false
上記のように、変数を宣言した後に=を追加し、その後に変数型と互換性のある初期値を指定すればよい。値を変更したい場合は、変数名の後に=と同じ型の別の値を指定するだけでよい。
varExercise.goファイルを変更します。
package main
import "fmt"
func main() {
// 宣言と初期化
var a int = 1
var b string = "labex"
var c bool = true
// 変数を出力
fmt.Println("変更前:")
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
// 変数を変更
a = 233
b = "labex"
c = false
// 変更後の変数を出力
fmt.Println("変更後:")
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
}
コードを実行すると、以下の出力になります。
$ go run varExercise.go
変更前:
1
labex
true
変更後:
233
labex
false
自分で実行して初期値を変更してみてください。
先ほど言ったように、変数に割り当てる初期値は変数宣言と同じ型でなければならない。異なる場合にはどうなるでしょうか。
たとえば、a変数に"labex"を初期値として割り当ててみましょう。
package main
import "fmt"
func main() {
var a int = "labex"
fmt.Println(a)
}
コードを実行すると:
$ go run varExercise.go
## command-line-arguments
./varExercise.go:6:12: cannot use "labex" (type untyped string) as type int in assignment
図のように、int型の変数に"labex"のような文字列型を割り当てることはできない。これは、Go が強力な型付きのコンパイル言語であり、コンパイルできないためである。
Go は初期値によって変数の型を決定できるので、型を明示的に指定するステップを省略することで、型宣言のプロセスを簡略化できますか?
package main
import "fmt"
func main() {
// var a int = 1
var a = 1 // 型が推論される
fmt.Println(a)
}
今では、変数を定義する際にvarキーワードさえ必要ありません。
このような変数の宣言と初期化の方法は、一括宣言方法と組み合わせることもできます。
a, b, c := 0
// 変数 a, b, c を整数型として宣言し、初期値を 0 とする
a, b, c := 0, "", true
// 変数 a, b, c をそれぞれ整数型、文字列型、ブール型として宣言する
短い宣言は非常に便利ですが、:=は代入演算子ではないことに注意してください。これは変数を宣言する方法であり、Go に固有のもので、関数内のローカル変数を宣言および初期化するために使用されます。変数の型は式に基づいて自動的に推論されます。
時々、次のようなコードを書きます。
func main() {
a := 1
println(a)
a := 2
println(a)
}
コンパイラはコードにエラーがあると言います。なぜなら、変数aが再宣言されているからです。ただし、次のように書くと:
func main() {
a := 1
if true {
a := 2
println(a) // 出力:2
}
println(a) // 出力:1
}
このような出力があるのは、上の値が1のaと下の値が2のaが同じ変数スコープ(同じ波括弧内)にないため、コンパイラはそれらを 2 つの異なる変数として扱うからです。
コンパイラはあなたの間違いを指摘しませんが、予期しない出力があります。
Go では、次のように規定されています。
関数の外の各文はキーワード(var、funcなど)で始める必要があります。
したがって、短い変数宣言はローカル変数の宣言にのみ使用でき、グローバル変数の宣言には使用できません。
では、グローバル変数とは何か、ローカル変数とは何か?
これは変数の生存期間の概念に関係しており、次のセクションで説明します。
変数のスコープとは、プログラム内の変数が有効な範囲、つまりどのように使用できるかを指します。
宣言した変数を使用しない場合、コードがコンパイルされないことに気付いたはずです。
言い換えると、Go をコンパイルする際には、各変数が使用されているか、つまりそのスコープ内で使用されているかどうかがチェックされます。
宣言位置に基づいて、変数を簡単に 3 つのタイプに分類できます。
このセクションでは、定義する変数の大部分はローカル変数です。
package main
import "fmt"
func main() { // 関数本体
var a int = 1 // ローカル変数
fmt.Println(a)
}
ローカル変数は関数本体の中で定義されます。たとえば、main関数内で定義されたaのように。変数aのスコープはmain関数内に限定されます。
同時に、main関数内で変数が使用されていない場合、コンパイラはエラーを投げます。
ただし、グローバル変数を定義することもできます。
package main
import "fmt"
var a int = 1 // グローバル変数
func main() { // 関数本体
fmt.Println(a)
}
グローバル変数は関数本体の外で定義され、そのスコープはプログラム全体をカバーします。どの関数でも呼び出されていなくても、コンパイラはエラーを報告しません。
グローバル変数が呼び出されていなくてもエラーにならない理由を考えてみてください。
これは、グローバル変数が別のパッケージで呼び出される可能性があるからです。
形式パラメータ変数に関する詳細は、後続の関数関連のコースで説明されます。
飛び去る鳥は影を隠す。狡兎死に、走狗烹る。 - 史記
変数が目的を果たしたら、メモリ使用量を削減するために破棄する必要があります。
このような設計が、Go の高性能と効率的な空間利用の鍵となっています。
変数の生存期間中は、再宣言することはできません。
varExercise.goに次のように書くことができます。
package main
import "fmt"
func main() {
var a int = 1 // ローカル変数、生存期間は main 関数全体に限定されます
var a int = 2 // 再定義
fmt.Println(a)
}
コードを実行した後:
go run varExercise.go
次のエラーメッセージが表示されます。
./varExercise.go:7:9: a redeclared in this block
previous declaration at./varExercise.go:6:9
コンパイラがaが再定義されていることを教えてくれます。
人生の多くのことは定数のようなものです。私たちはそれを認識することはできますが、変更することはできません。
プログラムの実行全体を通して変更されない変数の場合、それを定数として定義する必要があります。
定数は変数と非常によく似ており、不変の値を持つ変数と考えることもできます。
定数を宣言する際には、var キーワードを const キーワードに置き換えるだけで済みます。
const Pi = 3.14159 // 型推論による初期化 (Using type inference initialization)
~/project ディレクトリに constExercise.go という名前のファイルを作成します。
touch ~/project/constExercise.go
定数を変更しようとするとどうなるでしょうか?
package main
import "fmt"
func main() {
const Pi = 3.14159
Pi = 2 // Error: cannot assign to Pi
fmt.Println(Pi)
}
コードを実行します。
$ go run constExercise.go
## command-line-arguments
./constExercise.go:7:8: cannot assign to Pi
コンパイラは、Pi の値を再代入できないことを教えてくれます。
定数を宣言する際には、必ず初期値を指定する必要があります。
そして、定数に割り当てられる初期値は、コンパイル時に固定されている必要があります。
ユーザー定義関数の戻り値は、Go では固定されているとは見なされません。
var a int = 1
// 値は固定されており、宣言は有効です。
const Pi = 3.14159
// 計算された値も固定されており、宣言は有効です。
const c = 1 / Pi
// 組み込み関数からの固定された戻り値は有効です。
const le = len("labex")
// ユーザー定義関数の戻り値は固定されておらず、宣言は無効です。
const le = getLen("labby")
// `a` は固定されていない変数値であり、宣言は無効です。
const k = a
定数宣言が有効かどうかは、次の表にまとめられます。
| 宣言の種類 | 有効性 |
|---|---|
| 固定値と固定値の式 | 有効 |
| 非固定値(変数)およびそれに対応する式 | 無効 |
組み込み関数 (len()) が固定値と固定値の式を受け取る場合 |
有効 |
| ユーザー定義関数 | 無効 |
この実験で学んだことを振り返りましょう。
この実験では、Go における変数の基本的な使い方を振り返り、さまざまな状況で変数を宣言および使用する方法を示し、定数を紹介しました。