はじめに
データ型は、プログラムを書く際に特定の変数が格納する値の種類を指定します。データ型は、データに対してどのような操作が可能かも決定します。
この記事では、Goにネイティブな重要なデータ型について説明します。これはデータ型の完全な調査ではありませんが、Goで利用可能なオプションに慣れるのに役立ちます。基本的なデータ型を理解することで、効率的に実行される明確なコードを書くことができます。
背景
データ型について考える一つの方法は、現実世界で使用するさまざまな種類のデータを考えることです。現実世界のデータの例としては、数字があります:整数(0, 1, 2, …)、整数(…, -1, 0, 1, …)、無理数(π)などがあります。
通常、数学では異なる種類の数値を組み合わせて何らかの答えを得ることができます。例えば、5をπに加えたい場合があります:
5 + π
無理数を考慮して答えとして方程式を維持するか、πを少数点以下の桁数が短縮された数値に丸めてから数値を加算することができます:
5 + π = 5 + 3.14 = 8.14
しかし、もし数字を他のデータ型、例えば単語で評価しようとすると、意味があまり通らなくなります。次のような方程式をどのように解くのでしょうか?
shark + 8
コンピュータにとって、それぞれのデータ型は非常に異なります—例えば単語と数字のように。その結果、値を割り当てるためにさまざまなデータ型をどのように使用するか、そして操作を通じてそれらをどのように操作するかに注意する必要があります。
整数
数学と同様に、コンピュータプログラミングにおける整数は正、負、または0の全体数です(…, -1, 0, 1, …)。Go言語では、整数はint
として知られています。他のプログラミング言語と同様に、4桁以上の数字にカンマを使用してはいけません。そのため、プログラム内で1,000と書く場合は、1000
と書くべきです。
整数を次のように簡単に出力することができます:
Output-459
または、変数を宣言することもできます。この場合、変数は使用または操作している数字のシンボルです:
Output-459
整数を使ってGoで数学的な操作を行うこともできます。次のコードブロックでは、:=
代入演算子を使用して変数sum
を宣言し、インスタンス化します:
Output48
出力が示すように、数学的演算子-
は整数68
を116
から引き、結果として48
を得ました。変数の宣言については、変数のデータ型の宣言のセクションで詳しく学びます。
Goプログラム内で整数は多くの用途で使用されます。Goについて学び続ける中で、整数を扱い、このデータ型に関する知識をさらに深める機会がたくさんあります。
浮動小数点数
浮動小数点数またはfloatは、整数で表現できない実数を表すために使用されます。実数にはすべての有理数と無理数が含まれるため、浮動小数点数は9.0や-116.42のような小数部分を含むことができます。Goプログラムでfloatを考える際には、小数点を含む数値として捉えることができます。
整数の場合と同様に、浮動小数点数を次のように簡単に出力することができます:
Output-459.67
また、次のように浮動小数点数を表す変数を宣言することもできます:
Output-459.67
整数と同様に、Goでも浮動小数点数を使って数学的な計算を行うことができます:
Output929.24
整数と浮動小数点数では、3 ≠ 3.0であることに注意が必要です。3は整数を指すのに対し、3.0は浮動小数点数を指します。
数値型のサイズ
整数と浮動小数点数の区別に加えて、Goにはサイズの静的または動的な性質によって区別される2種類の数値データ型があります。最初のタイプはアーキテクチャに依存しないタイプで、ビット単位のデータサイズは、コードが実行されているマシンに関係なく変わりません。
今日のほとんどのシステムアーキテクチャは32ビットまたは64ビットです。例えば、最新のWindowsラップトップ向けに開発しているかもしれませんが、そのオペレーティングシステムは64ビットアーキテクチャ上で動作しています。しかし、フィットネスウォッチのようなデバイス向けに開発している場合は、32ビットアーキテクチャで作業しているかもしれません。int32
のようなアーキテクチャに依存しないタイプを使用すると、コンパイル対象のアーキテクチャに関係なく、そのタイプは一定のサイズを持ちます。
2番目のタイプは実装固有のタイプです。このタイプでは、プログラムが構築されるアーキテクチャに基づいてビットサイズが変わる可能性があります。例えば、int
タイプを使用する場合、Goが32ビットアーキテクチャ向けにコンパイルすると、データ型のサイズは32ビットになります。プログラムが64ビットアーキテクチャ向けにコンパイルされる場合、変数は64ビットのサイズになります。
データ型のサイズが異なるだけでなく、整数のような型も符号付きと符号なしの2つの基本タイプがあります。int8
は符号付き整数で、-128から127までの値を持つことができます。uint8
は符号なし整数で、0から255までの正の値のみを持つことができます。
範囲はビットサイズに基づいています。バイナリデータの場合、8ビットは合計256の異なる値を表すことができます。int
型は正の値と負の値の両方をサポートする必要があるため、8ビット整数(int8
)の範囲は-128から127までとなり、合計256のユニークな可能な値があります。
Goには以下のアーキテクチャに依存しない整数型があります:
uint8 unsigned 8-bit integers (0 to 255)
uint16 unsigned 16-bit integers (0 to 65535)
uint32 unsigned 32-bit integers (0 to 4294967295)
uint64 unsigned 64-bit integers (0 to 18446744073709551615)
int8 signed 8-bit integers (-128 to 127)
int16 signed 16-bit integers (-32768 to 32767)
int32 signed 32-bit integers (-2147483648 to 2147483647)
int64 signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
浮動小数点数と複素数もまた、様々なサイズで提供されています:
float32 IEEE-754 32-bit floating-point numbers
float64 IEEE-754 64-bit floating-point numbers
complex64 complex numbers with float32 real and imaginary parts
complex128 complex numbers with float64 real and imaginary parts
また、特定のデータ型に有用な名前を割り当てるエイリアス数値型もいくつかあります:
byte alias for uint8
rune alias for int32
byte
エイリアスの目的は、プログラムがバイトデータ測定とは無関係の小さな整数ではなく、文字列要素として一般的なコンピューティング測定値としてバイトを使用していることを明確にすることです。byte
とuint8
はプログラムがコンパイルされると同一になりますが、byte
は数値形式で文字データを表すためによく使用され、uint8
はプログラム内の数値として意図されています。
rune
エイリアスは少し異なります。byte
とuint8
はまったく同じデータですが、rune
は1バイトまたは4バイトになることができ、int32
によって決定される範囲です。rune
はUnicode文字を表すために使用され、ASCII文字のみがint32
データ型だけで表すことができます。
さらに、Goには以下の実装固有の型があります:
uint unsigned, either 32 or 64 bits
int signed, either 32 or 64 bits
uintptr unsigned integer large enough to store the uninterpreted bits of a pointer value
実装固有の型は、プログラムがコンパイルされるアーキテクチャによってサイズが定義されます。
数値データ型の選択
適切なサイズを選ぶ際には、通常、対象とするアーキテクチャのパフォーマンスが、扱うデータのサイズよりも重要です。しかし、プログラムのパフォーマンスに関する具体的な影響を知らなくても、最初の段階では以下のような基本的なガイドラインに従うことができます。
この記事で前述したように、アーキテクチャに依存しない型と、実装に固有の型があります。整数データについては、Goではint64
やuint64
ではなく、int
やuint
のような実装型を使用するのが一般的です。これにより、通常、対象とするアーキテクチャで最速の処理速度が得られます。例えば、int64
を使用して32ビットアーキテクチャにコンパイルすると、データをアーキテクチャ間で移動するための追加のCPUサイクルが必要になるため、少なくとも2倍の時間がかかります。代わりにint
を使用すれば、プログラムは32ビットアーキテクチャで32ビットのサイズとして定義され、処理が大幅に速くなります。
特定のサイズ範囲を超えないことがわかっている場合、アーキテクチャに依存しない型を選ぶことで、速度を向上させ、メモリ使用量を削減できます。例えば、データが100
を超えず、正の数のみであることがわかっている場合、uint8
を選ぶことでプログラムの効率が向上し、メモリ使用量が少なくなります。
数値データ型の可能な範囲をいくつか見てきたので、プログラムでそれらの範囲を超えた場合に何が起こるかを見てみましょう。
オーバーフロー vs. ラップアラウンド
Goは、値がコンパイル時に計算されるか実行時に計算されるかに応じて、データ型が格納するように設計された値よりも大きな値を格納しようとすると、数値をオーバーフローさせる可能性とラップアラウンドさせる可能性の両方があります。コンパイル時エラーは、プログラムがビルドしようとしたときにエラーを見つけた場合に発生します。実行時エラーは、プログラムがコンパイルされた後、実際に実行されている間に発生します。
次の例では、maxUint32
をその最大値に設定します:
これはコンパイルされて実行され、以下の結果をもたらします:
実行時に1
を値に加えると、それは0
にラップアラウンドします:
一方、コンパイル時に変数に1
を加えてから代入するようにプログラムを変更してみましょう:
コンパイル時に、コンパイラが指定されたデータ型に保持するには値が大きすぎると判断した場合、overflow
エラーをスローします。これは、計算された値が指定したデータ型には大きすぎることを意味します。
コンパイラが値がオーバーフローすると判断できるため、今度はエラーをスローします:
Outputprog.go:6:36: constant 4294967296 overflows uint32
データの範囲を理解することで、将来プログラムで潜在的なバグを回避するのに役立ちます。
数値型について学んだので、次にブール値をどのように保存するかを見てみましょう。
ブール値
ブールデータ型は、true
またはfalse
の2つの値のいずれかであり、データ型として宣言する場合はbool
と定義されます。ブール値は、コンピュータサイエンスにおけるアルゴリズムに情報を提供する数学の論理分野に関連する真理値を表すために使用されます。
true
とfalse
の値は、Goでは事前に宣言された識別子であるため、それぞれ小文字のt
とf
で表記されます。
数学の多くの演算は、真または偽のいずれかに評価される答えを与えます:
- 大なり
- 500 > 100 true
- 1 > 5 false
- 小なり
- 200 < 400 true
- 4 < 2 false
- 等しい
- 5 = 5 true
- 500 = 400 false
数値と同様に、ブール値を変数に格納することができます:
そして、fmt.Println()
関数を呼び出すことでブール値を出力できます:
5
は8
より大きくないため、次のような出力が得られます:
Outputfalse
Goでより多くのプログラムを書くにつれて、ブール値の仕組みや、true
またはfalse
と評価されるさまざまな関数や操作がプログラムの流れをどのように変えるかについて、より理解が深まるでしょう。
文字列
文字列は、一つ以上の文字(文字、数字、記号)の並びで、定数または変数となり得ます。文字列はGoではバッククォート`
または二重引用符"
のいずれかで囲まれ、使用する引用符によって特性が異なります。
バッククォートを使用すると、生の文字列リテラルを作成します。二重引用符を使用すると、解釈された文字列リテラルを作成します。
生の文字列リテラル
生の文字列リテラルは、バッククォート(バックティックとも呼ばれる)で囲まれた文字の並びです。引用符内のどの文字も、バッククォート文字自体を除いて、バッククォート間に表示される通りに表示されます。
OutputSay "hello" to Go!
通常、バックスラッシュは文字列内の特殊文字を表すために使用されます。例えば、解釈される文字列内で、\n
は文字列内の新しい行を表します。しかし、バックスラッシュは生の文字列リテラル内では特別な意味を持ちません:
文字列リテラル内でバックスラッシュに特別な意味がないため、実際には\n
の値を出力し、新しい行を作成しません:
OutputSay "hello" to Go!\n
生の文字列リテラルは、複数行の文字列を作成するためにも使用できます:
OutputThis string is on
multiple lines
within a single back
quote on either side.
前述のコードブロックでは、新しい行が入力から出力へそのまま引き継がれました。
解釈される文字列リテラル
解釈される文字列リテラルは、"bar"
のようにダブルクォーテーションで囲まれた文字列です。クォーテーション内では、改行とエスケープされていないダブルクォーテーションを除くあらゆる文字が現れることがあります。解釈される文字列内でダブルクォーテーションを表示するには、次のようにバックスラッシュをエスケープ文字として使用できます:
OutputSay "hello" to Go!
ほとんどの場合、解釈される文字列リテラルを使用します。なぜなら、それらはエスケープ文字を許可しているからです。文字列の扱い方について詳しくは、Goでの文字列操作の紹介をチェックしてください。
UTF-8文字を含む文字列
UTF-8は、文字を1バイトから4バイトまでの可変幅でエンコードするためのエンコーディング方式です。Goは特別なセットアップ、ライブラリ、パッケージなしでUTF-8文字をサポートしています。文字A
のようなローマ字は、数字65のようなASCII値で表現できます。しかし、世
のような国際的な特殊文字では、UTF-8が必要になります。GoはUTF-8データにrune
というエイリアス型を使用します。
Goでは、for
ループ内でrange
キーワードを使用して、UTF-8文字列を含む任意の文字列をインデックス化することができます。for
ループとrange
については、後のシリーズでより深く説明しますが、現時点では、これを使用して特定の文字列のバイト数をカウントできることを知っておくことが重要です。
上記のコードブロックでは、変数a
を宣言し、それにHello, 世界
という値を代入しました。代入されたテキストにはUTF-8文字が含まれています。
その後、標準のfor
ループとrange
キーワードを使用しました。Goでは、range
キーワードは文字列をインデックス化し、一度に1文字ずつ、そしてその文字が文字列内のどのバイトインデックスにあるかを返します。
fmt.Printf
関数を使用して、フォーマット文字列%d: %s\n
を提供しました。%d
は数字(この場合は整数)のプリント動詞であり、%s
は文字列のプリント動詞です。次に、for
ループの現在のインデックスであるi
と、for
ループの現在の文字であるc
の値を提供しました。
最後に、組み込みのlen
関数を使用して、変数a
の全体の長さを出力しました。
以前に、ルーンはint32
のエイリアスであり、1から4バイトで構成されることを述べました。世
という文字は3バイトで定義され、UTF-8文字列を範囲指定する際にインデックスがそれに応じて移動します。これが、i
が出力されるときに連続していない理由です。
Output0: H
1: e
2: l
3: l
4: o
5: ,
6:
7: 世
10: 界
length of 'Hello, 世界': 13
ご覧のように、長さは文字列を範囲指定するのにかかった回数よりも長くなっています。
常にUTF-8文字列を扱うわけではありませんが、UTF-8文字列を扱う場合は、それらがルーンであり、単一のint32
ではない理由を理解できるようになりました。
変数のデータ型を宣言する
異なるプリミティブデータ型について知ったので、Goでこれらの型を変数に割り当てる方法を説明します。
Goでは、キーワードvar
に続けて変数の名前と希望するデータ型を指定することで、変数を定義できます。
以下の例では、float64
型のpi
という変数を宣言します。
キーワードvar
が最初に宣言されます:
次に、変数の名前pi
が続きます:
そして最後にデータ型float64
が宣言されます:
オプションで、初期値を指定することもできます。例えば3.14
:
Goは静的型付け言語です。静的型付けとは、プログラム内の各ステートメントがコンパイル時にチェックされることを意味します。また、データ型が変数に関連付けられることを意味しますが、動的型付け言語では、データ型は値に関連付けられます。
例えば、Goでは、変数を宣言する際に型が宣言されます:
これらの変数は、宣言方法によって異なるデータ型になる可能性があります。
これは、データ型が値に関連付けられるPHPのような言語とは異なります:
前述のコードブロックでは、最初の$s
は値"sammy"
が割り当てられているため文字列であり、2番目は値123
を持つため整数です。
次に、配列のようなより複雑なデータ型を見てみましょう。
配列
配列は、要素の順序付きシーケンスです。配列の容量は作成時に定義されます。一度配列がそのサイズを割り当てると、サイズを変更することはできません。配列のサイズは静的であるため、メモリを一度だけ割り当てることを意味します。これにより、配列は扱いにくいものになりますが、プログラムのパフォーマンスが向上します。このため、配列は通常、プログラムの最適化時に使用されます。スライスは、次に説明するように、より柔軟性があり、他の言語での配列と考えられるものです。
配列は、配列のサイズを宣言し、次にデータ型を宣言し、値を波括弧{ }
で定義することで定義されます。
文字列の配列は次のようになります:
配列を変数に格納して出力することができます:
Output[blue coral staghorn coral pillar coral]
前述のように、スライスは配列に似ていますが、はるかに柔軟です。この可変データ型を見てみましょう。
スライス
スライスは、長さを変更できる要素の順序付きシーケンスです。スライスはサイズを動的に増やすことができます。新しいアイテムをスライスに追加するとき、スライスに新しいアイテムを格納するのに十分なメモリがない場合、必要に応じてシステムからより多くのメモリを要求します。スライスは、必要に応じて要素を追加するために拡張できるため、配列よりも一般的に使用されます。
スライスは、データ型の前に開きと閉じの角括弧 `[]
` を付け、波括弧 `{ }
` の間に値を持つことで定義されます。
整数のスライスは次のようになります:
浮動小数点数のスライスは次のようになります:
文字列のスライスは次のようになります:
文字列のスライスを `seaCreatures
` として定義しましょう:
変数を呼び出すことでそれらを出力できます:
出力は、作成したリストとまったく同じように見えます:
Output[shark cuttlefish squid mantis shrimp]
スライスにアイテムを追加するために `append
` キーワードを使用できます。次のコマンドは、スライスに文字列値 `seahorse
` を追加します:
出力して確認できます:
Output[shark cuttlefish squid mantis shrimp seahorse]
ご覧のように、未知のサイズの要素を管理する必要がある場合、スライスは配列よりもはるかに柔軟です。
マップ
`map` は Go の組み込みのハッシュまたは辞書型です。マップは `キー` と `値` をペアで使用してデータを格納します。これは、インデックス(この場合はキー)によって値を迅速に検索するプログラミングにおいて便利です。たとえば、ユーザー ID でインデックス付けされたユーザーのマップを保持したい場合があります。キーはユーザー ID になり、ユーザーオブジェクトが値になります。マップは、キーワード `map
` を使用し、その後にキーのデータ型を角括弧 `[ ]
` で囲み、値のデータ型を続け、波括弧内にキーと値のペアを配置することで構築されます。
map[key]value{}
通常、関連性のあるデータを保持するために使用され、例えばIDに含まれる情報のようなものです。マップは以下のようになります:
波括弧に加えて、マップ内にはコロンが散在していることに気づくでしょう。コロンの左側の単語はキーです。キーはGo言語内の任意の比較可能な型であり得ます。比較可能な型は、strings
、ints
などのプリミティブ型です。プリミティブ型は言語によって定義され、他の型を組み合わせることで構築されるものではありません。ユーザー定義型であることも可能ですが、プログラミングエラーを避けるためにはシンプルに保つことがベストプラクティスとされています。上記の辞書のキーは、name
、animal
、color
、およびlocation
です。
コロンの右側の単語は値です。値は任意のデータ型で構成され得ます。上記の辞書の値は、Sammy
、shark
、blue
、およびocean
です。
マップを変数内に格納し、それを出力してみましょう:
Outputmap[animal:shark color:blue location:ocean name:Sammy]
もしSammyの色を分離したい場合、sammy["color"]
を呼び出すことでそれが可能です。それを出力してみましょう:
Outputblue
マップはデータをキーと値のペアで保存するため、Goプログラムにおいて重要な要素となり得ます。
結論
この時点で、Goで使用可能な主要なデータ型のいくつかをより良く理解しているはずです。これらの各データ型は、Go言語でプログラミングプロジェクトを開発する際に重要になります。
Goで利用可能なデータ型をしっかりと理解したら、状況に応じてデータ型を変更するためにデータ型の変換方法を学ぶことができます。
Source:
https://www.digitalocean.com/community/tutorials/understanding-data-types-in-go