「基本データ型」
書籍:プログラミング言語Go
第3章 「基本データ型」 の要点と思われる箇所を自分のメモ用にまとめました。
3.1 整数
- int, uintは32か64bitだが、そのサイズがどちらであるか想定した実装をしてはいけない。コンパイラで変わりうるから。
- uintptr は幅の規定がない。ポインタの値のすべてのビットを保持するのに十分な幅は持つ。
- 演算子は結びつきの優先度が5段階ある。同じ優先度の演算子が並ぶ場合、左に結び付く。わかりやすさのため () でくくること推奨。
- 余剰演算子 % は整数のみ利用可。負の余剰の振る舞いは言語で異なり、Goでは剰余の符号は被除数 (%の左の数値) の符号と同じになる。
- オーバーフロー時は方に収まらない上位ビットが破棄される。
- 符号付数値の右へのビットシフトは空いたビットに符号ビットをコピーして埋める。整数をビットパターンとして扱う場合、符号なし算術を使うことが重要。
- sliceのlenは明らかに正の整数だが、int型。for文の初期化節でlen使った際にuintで返されると、iがuintになり、思わぬ動作になりうる。
- 上記などを理由に符号なし数値は単なる負ではない数量に対しては普通は使われない。
- 符号なし数値はビットセットの実装、バイナリファイル形式の解析、ハッシュや暗号化のビット演算子、普通と異なる算術演算子が必要な場合用いられる。
- T(x)により大きい整数から小さい整数型に変換したり、整数を浮動小数点型に(逆も)変換する場合、値が変更されたり精度を失いうる。
- Printfのverbで%の後に[1]とすると、第一オペランドが利用される。
.go
fmt.Printf("%d, %[1]x", x) // %d, %[1]x ともに第一オペランド x が利用される。
3.2 浮動小数点数
- 浮動小数点数の限界値は mathパッケージ に定義されている。
- float32は6桁の、float64は15桁の10進の精度を有す。float32は誤差が大きいのでfloat64を使うのが好ましい。
- mathパッケージには正の無限大、負の無限大、NaN(not a number:0/0などの数学的に疑わしい演算の結果) の定義などがある。
- math,NaN() 同士の比較演算結果は != を除いて常にfalseになるので注意。
.go
nan := math.NaN()
if nan == nan { // false
if nan < nan { // false
if nan > nan { // false
3.3 複素数
- complex64, complex128はそれぞれ実部、虚部がfloat32とfloat64の複素数。
- x := 1 + 2i のように、iをつけると虚数。var x complex128 := complex(1, 2) と同じ。
- math.complexパッケージで複素数の算術を行える。
3.4 ブーリアン
- && は || より優先順位が高い。&&はブーリアン乗算、||はブーリアン加算と覚えるとよい。
3.5 文字列
- len関数は文字列中のバイト数を返す。
- ASCIIでないコードポイントのUTF-8は2バイト以上必要(1文字が2バイト以上) となるので注意。
- 文字列の比較演算は辞書順。
- 文字列は不変なので、文字列のデータを直接変更する構文は許されない。
s[0] = 'L' // コンパイルエラー: s[0] には代入できない。
3.5.1 文字列リテラル
- Goソースファイルは常にUTF8でエンコードされる。
- ``で囲うと生文字列リテラルとなり、CR(キャリッジリターン) の削除以外は、``で囲われた文字列がそのまま利用される。エスケープシーケンスも処理されない。改行を含む文字の表示(usage表示とか)に便利。
3.5.2 Unicode
- Unicodeは世界中の文字をサポートしていて、各文字にUnicodeコードポイント(Go用語ではルーン) と呼ばれる規格番号を割り当てる。
3.5.3 UTF-8
- UTF-8はルーン表現のために1から4バイトを利用する。ASCIIは1バイト、その他は最初のバイトの最上位数ビットにその後何バイト続くか示される。
0xxxxxxx :ASCII
110xxxxx 10xxxxxx :2バイト文字
1110xxxx 10xxxxxx 10xxxxxx :3バイト文字
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx :4バイト文字
- 上記設計により4バイト以上戻ることなく文字の開始を見つけることができる。
- Unicodeエスケープを使えばコードポイントの数値で文字を指定できる。
- コードポイント(ルーン:実際の1文字, 1文字)単位で文字列操作をする場合、unicode/utf8 パッケージのデコーダを使う。
- Goのrangeが文字列へ適用された場合、UTF-8のデコードを暗黙的に行う。以下でルーン数を簡単に数えられる。
.go
s := "Hello, 世界" // 9文字。(len(s) は13)
n := 0
for _, _ := range s {
n++
}
fmt.Println(n) // 9
- UTF-8デコーダが期待しない入力バイトを受け取ると '\uFFFD' (◆の中に白い? がある文字) を生成する。この文字が表示されるとどこかでテキストエンコーディングが不正になっている。
- UTF-8でエンコードされた文字列に []rune を適用できる。プログラム中でUTF-8文字列を扱うのに便利。
3.5.4 文字列とバイトスライス
- 文字列は不変のため文字列を少しずつ増やしながら構築することは多数の割り当てとコピーを必要とする。このような場合はbytes.Bufferが有効。
- strconvパッケージは数値方を文字列変換するなどの関数を提供。
- unicodeパッケージはルーンを分類するIsDigit, IsLetter, IsUpper, IsLowerなどの関数を提供。各関数は単一のルーンの引数を受け取る。
- basenameはUnixのユーティリティからヒントを得た機能で、渡されたファイルパスを"/"で要素分割し、拡張子を除いたファイル名を得る。
.go
fmt.Println(basename("a/b/c.go") // "c"
fmt.Println(basename("c.d.go") // "c.d"
fmt.Println(basename("abc") // "abc"
- 文字列 s を用いた []bytes(s) 変換はsのバイトのコピーを保持する新たなバイト配列を割り当てる。コピーによりbytes変換したスライスを操作しても元の s には影響しない。
- bytesはスライスを効率的に操作するためのBuffer型を提供する。BufferにUTF-8のルーンを書き込む際はWiteRuneを使う。ASCIIであればWriteByteで構わない。
3.5.5 文字列と数値の変換
- strconvを使うほかにfmt.Sprintf()でも数値を文字列変換できる。Sprintfは%b, %xなどのverbを使え、また、数値以外に追加情報を加えられる。
.go
x := 123
s := fmt.Sprintf("x=%b", x) // "x=1111011"
- ParseIntは基数 (2進、10進など) と戻り値として許す最大値のbitサイズを指定できる。
3.6 定数
- 定数はコンパイル時に評価が行われる式のため実行時の処理を減らす。範囲外インデックスの参照や無限大の浮動小数点演算などのエラーもコンパイル時に検出。
- すべての定数の基底型はブーリアン、文字列、数値の基本型。
- 定数constは () で囲うことができ、関連する定数をグループ化する場合に適する。
- 定数式の型が明示的に指定されない場合、右辺から推定される。
- 定数をグループで宣言する場合、最初の定数以外、右辺を省略可能。iota を使うと便利な場面が出てくる。
.go
const (
a = 1 // 1
b // 1
c = 2 // 2
d // 2
)
3.6.1 定数生成器 iota
- iotaは明示的に毎回書くことなく関連した値を生成するために使う。const宣言で使うとゼロから1ずつ増加する。
.go
type Weekday int
const (
Sunday Weekend = iota
Monday
Tuesday
...
- 以下のような使い方もできる。以下はuintの下位5ビットに別々の名前を付けてブーリアンとして解釈している。
.go
type Flags uint
const (
FlagUp Flag = 1 << iota
FlagBroadcast // func SetBroadcast(v *Flags) { *v |= FlagBroadcast } 利用例の実装
FlagLoopback
FlagPointToPoint
FlagMulticast
)
- 1024の累乗に名前を付けた例。
.go
const (
_ = 1 << (10 * iota)
KiB // 1024 (fmt.Printf("%b", KiB) は 10000000000)
MiB // 1048576
...
3.6.2 型付けなし定数
- 多くの定数は特定の型に結びついているわけではなく、そのような定数は基本型よりはるかに高い数値精度で表現される。
- 型への結びつけを遅延させることで、型付けなし定数は高い精度を有し、また、多くの式で変換を必要とせず使える。
.go
var x float32 = math.Pi
var y float64 = math.Pi
var z complex128 = math.Pi
- 非対称性に注意。整数の型付けなし定数を望むサイズの型で利用したいときは明示的な変換を使う。
.go
var i = int8(0)
練習問題解答例