Goのデータ型について

前書き

データ型は、プログラムを作成するときに特定の変数が格納する値の種類を指定します。 データ型は、データに対して実行できる操作も決定します。

この記事では、Go固有の重要なデータ型について説明します。 これは、データ型の網羅的な調査ではありませんが、Goで使用できるオプションを理解するのに役立ちます。 いくつかの基本的なデータ型を理解すると、効率的に実行するより明確なコードを書くことができます。

バックグラウンド

データ型について考える1つの方法は、現実の世界で使用するさまざまな種類のデータを考慮することです。 実世界のデータの例は数字です。たとえば、整数(0、1、2、…)、整数(…、-1、0、1、…)、無理数(π)を使用できます。

通常、数学では、異なるタイプの数値を組み合わせて、何らかの答えを得ることができます。 たとえば、πに5を追加します。

5 + π

無理数を説明するために方程式を答えとして保持するか、πを小数点以下の桁数が省略された数に丸めてから、その数を加算します。

5 + π = 5 + 3.14 = 8.14

しかし、単語などの別のデータ型で数値を評価しようとすると、物事はあまり意味がなくなります。 次の方程式をどのように解きますか?

shark + 8

コンピュータの場合、単語や数字など、各データ型はまったく異なります。 そのため、さまざまなデータ型を使用して値を割り当てる方法と、操作を介して値を操作する方法に注意する必要があります。

整数

数学と同様に、コンピュータープログラミングのintegersは、正、負、または0(…、-1、0、1、…)の整数です。 Goでは、整数はintとして知られています。 他のプログラミング言語と同様に、4桁以上のコンマは使用しないでください。したがって、プログラムで1,000を書き込む場合は、1000として記述してください。

次のような簡単な方法で整数を出力できます。

fmt.Println(-459)
Output-459

または、変数を宣言することもできます。この場合、次のように、使用または操作している数値のシンボルです。

var absoluteZero int = -459
fmt.Println(absoluteZero)
Output-459

Goでも整数を使用して数学を実行できます。 次のコードブロックでは、:=代入演算子を使用して、変数sumを宣言およびインスタンス化します。

sum := 116 - 68
fmt.Println(sum)
Output48

出力が示すように、数学演算子-116から整数68を減算し、結果として48になりました。 変数宣言について詳しくは、Declaring Data Types for Variablesセクションをご覧ください。

整数は、Goプログラム内でさまざまな方法で使用できます。 Goの学習を続けると、整数を操作し、このデータ型の知識に基づいて構築する多くの機会が得られます。

浮動小数点数

floating-point numberまたはfloatは、整数として表現できないreal numbersを表すために使用されます。 実数にはすべての有理数と無理数が含まれます。そのため、浮動小数点数には、9.0や-116.42などの小数部分が含まれることがあります。 Goプログラムのフロートを考える場合、小数点を含む数値です。

整数で行ったように、次のような簡単な方法で浮動小数点数を出力できます。

fmt.Println(-459.67)
Output-459.67

次のように、floatを表す変数を宣言することもできます。

absoluteZero := -459.67
fmt.Println(absoluteZero)
Output-459.67

整数の場合と同様に、Goでもfloatを使用して数学を実行できます。

var sum = 564.0 + 365.24
fmt.Println(sum)
Output929.24

整数と浮動小数点数では、3は整数を表し、3.0は浮動小数点を表すため、3≠3.0であることに留意することが重要です。

数値型のサイズ

整数と浮動小数点数の区別に加えて、Goにはサイズの静的または動的な性質によって区別される2種類の数値データがあります。 最初のタイプはarchitecture-independentタイプです。これは、コードが実行されているマシンに関係なく、ビット単位のデータのサイズが変更されないことを意味します。

現在のほとんどのシステムアーキテクチャは、32ビットまたは64ビットです。 たとえば、オペレーティングシステムが64ビットアーキテクチャで実行される最新のWindowsラップトップ用に開発している場合があります。 ただし、フィットネスウォッチなどのデバイス用に開発している場合は、32ビットアーキテクチャで作業している可能性があります。 int32のようなアーキテクチャに依存しない型を使用する場合、コンパイルするアーキテクチャに関係なく、型のサイズは一定になります。

2番目のタイプはimplementation-specificタイプです。 このタイプでは、ビットサイズはプログラムが構築されているアーキテクチャに基づいて異なる場合があります。 たとえば、int型を使用する場合、Goが32ビットアーキテクチャ用にコンパイルされると、データ型のサイズは32ビットになります。 プログラムが64ビットアーキテクチャ用にコンパイルされている場合、変数のサイズは64ビットになります。

サイズの異なるデータ型に加えて、整数などの型には、signedunsignedの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エイリアスの目的は、プログラムがバイトデータ測定に関係のない小さな整数ではなく、文字列要素の一般的な計算測定としてバイトを使用していることを明確にすることです。 プログラムのコンパイル後はbyteuint8は同一ですが、byteは文字データを数値形式で表すためによく使用されますが、uint8はあなたのプログラム。

runeエイリアスは少し異なります。 byteuint8がまったく同じデータである場合、runeは1バイトまたは4バイトであり、範囲はint32によって決定されます。 runeはUnicode文字を表すために使用されますが、int32データ型のみで表すことができるのはASCII文字のみです。

さらに、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を選択すると、必要なメモリが少なくなるため、プログラムの効率が向上します。

数値データ型の可能な範囲のいくつかを確認したので、プログラムでこれらの範囲を超えた場合にどうなるかを見てみましょう。

オーバーフローと 包み込む

Goは、値がコンパイル時と実行時のどちらで計算されるかに応じて、格納するように設計されたデータ型よりも大きい値を格納しようとすると、overflowwraparoundの両方の数値になる可能性があります。 コンパイル時エラーは、プログラムがプログラムをビルドしようとしたときにエラーを見つけると発生します。 プログラムが実際に実行されている間に、プログラムがコンパイルされた後に実行時エラーが発生します。

次の例では、maxUint32を最大値に設定します。

package main

import "fmt"

func main() {
    var maxUint32 uint32 = 4294967295 // Max uint32 size
    fmt.Println(maxUint32)
}

次の結果でコンパイルおよび実行されます。

Output4294967295

実行時に値に1を追加すると、0にラップアラウンドします。

Output0

一方、コンパイル時の前に、変数を割り当てるときに1を変数に追加するように、プログラムを変更しましょう。

package main

import "fmt"

func main() {
    var maxUint32 uint32 = 4294967295 + 1
    fmt.Println(maxUint32)

}

コンパイル時に、コンパイラが値が大きすぎて指定されたデータ型を保持できないと判断できる場合、overflowエラーがスローされます。 これは、計算された値が指定したデータ型に対して大きすぎることを意味します。

コンパイラは値をオーバーフローさせると判断できるため、エラーがスローされます。

Outputprog.go:6:36: constant 4294967296 overflows uint32

データの境界を理解することは、将来のプログラムの潜在的なバグを回避するのに役立ちます。

数値型について説明したので、ブール値を保存する方法を見てみましょう。

ブール値

booleanデータ型は、trueまたはfalseの2つの値のいずれかであり、データ型として宣言するときにboolとして定義されます。 ブールは、コンピューターサイエンスのアルゴリズムに情報を与える数学の論理分岐に関連付けられている真理値を表すために使用されます。

truefalseは、Goで事前に宣言された識別子であるため、常にそれぞれ小文字のtfになります。

数学の多くの演算は、trueまたはfalseに評価される答えを提供します。

  • より大きい

    • 500> 100真

    • 1> 5 false

  • 未満

    • 200 <400真

    • 4 <2 false

  • 等しい

    • 5 = 5 true

    • 500 = 400偽

数値と同様に、ブール値を変数に格納できます。

myBool := 5 > 8

次に、fmt.Println()関数を呼び出してブール値を出力できます。

fmt.Println(myBool)

58以下であるため、次の出力が返されます。

Outputfalse

Goでより多くのプログラムを作成するにつれて、ブール値がどのように機能するか、およびtrueまたはfalseのいずれかに評価されるさまざまな関数や操作がプログラムのコースをどのように変更できるかを理解するようになります。

文字列

文字列は、定数または変数のいずれかになる1つ以上の文字(文字、数字、記号)のシーケンスです。 文字列は、Goの逆引用符+\ + `または二重引用符"内に存在し、使用する引用符に応じて異なる特性を持ちます。

逆引用符を使用すると、raw文字列リテラルが作成されます。 二重引用符を使用すると、interpreted文字列リテラルが作成されます。

生の文字列リテラル

生の文字列リテラルは、バッククォートの間の文字シーケンスであり、しばしばバックティックと呼ばれます。 引用符内では、逆引用符自体を除き、任意の文字が逆引用符の間に表示されるように表示されます。

a := `Say "hello" to Go!`
fmt.Println(a)
OutputSay "hello" to Go!

通常、バックスラッシュは文字列内の特殊文字を表すために使用されます。 たとえば、解釈された文字列では、 は文字列の改行を表します。 ただし、生の文字列リテラル内ではバックスラッシュに特別な意味はありません。

a := `Say "hello" to Go!\n`
fmt.Println(a)

バックスラッシュは文字列リテラルでは特別な意味を持たないため、新しい行を作成する代わりに、実際には の値を出力します。

OutputSay "hello" to Go!\n

生の文字列リテラルを使用して、複数行の文字列を作成することもできます。

a := `This string is on
multiple lines
within a single back
quote on either side.`
fmt.Println(a)
OutputThis string is on
multiple lines
within a single back
quote on either side.

上記のコードブロックでは、新しい行は文字通り入力から出力に引き継がれました。

解釈された文字列リテラル

解釈される文字列リテラルは、"bar"のように、二重引用符で囲まれた文字シーケンスです。 引用符内では、改行およびエスケープされていない二重引用符を除く任意の文字が表示される場合があります。 解釈された文字列に二重引用符を表示するには、次のようにバックスラッシュをエスケープ文字として使用できます。

a := "Say \"hello\" to Go!"
fmt.Println(a)
OutputSay "hello" to Go!

ほとんどの場合、解釈された文字列リテラルを使用します。これは、その中にエスケープ文字を許可するためです。 文字列の操作の詳細については、An Introduction to Working with Strings in Goを確認してください。

UTF-8文字を含む文字列

UTF-8は、可変幅文字を1〜4バイトにエンコードするために使用されるエンコード方式です。 Goは、特別なセットアップ、ライブラリ、またはパッケージなしで、すぐに使用できるUTF-8文字をサポートします。 文字Aなどのローマ字は、数値65などのASCII値で表すことができます。 ただし、の国際文字などの特殊文字では、UTF-8が必要になります。 Goは、UTF-8データにruneエイリアスタイプを使用します。

a := "Hello, 世界"

forループでrangeキーワードを使用して、UTF-8文字列を含め、Goの任意の文字列にインデックスを付けることができます。 forループとrangeについては、シリーズの後半で詳しく説明します。今のところ、これを使用して特定の文字列のバイトをカウントできることを知っておくことが重要です。

package main

import "fmt"

func main() {
    a := "Hello, 世界"
    for i, c := range a {
        fmt.Printf("%d: %s\n", i, string(c))
    }
    fmt.Println("length of 'Hello, 世界': ", len(a))
}

上記のコードブロックでは、変数aを宣言し、それにHello, 世界の値を割り当てました。 割り当てられたテキストにはUTF-8文字が含まれています。

次に、標準のforループとrangeキーワードを使用しました。 Goでは、rangeキーワードは、一度に1文字を返す文字列と、その文字が文字列内にあるバイトインデックスを介してインデックスを作成します。

fmt.Printf関数を使用して、%d: %s のフォーマット文字列を提供しました。 %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文字列を使用しているとは限りませんが、使用している場合は、それらが単一のint32ではなくルーンである理由を理解できます。

変数のデータ型を宣言する

さまざまなプリミティブデータ型について理解したので、Goでこれらの型を変数に割り当てる方法について説明します。

Goでは、キーワードvarの後に変数の名前と必要なデータ型を指定して、変数を定義できます。

次の例では、タイプfloat64piという変数を宣言します。

キーワードvarは、最初に宣言されたものです。

var pi float64

変数の名前の後にpiが続きます。

var pi float64

そして最後に、データ型float64

var pi float64

オプションで、3.14などの初期値を指定することもできます。

var pi float64 = 3.14

Goはstatically typed言語です。 静的型付けとは、プログラムの各ステートメントがコンパイル時にチェックされることを意味します。 また、データ型は変数にバインドされますが、動的にリンクされた言語では、データ型は値にバインドされます。

たとえば、Goでは、変数を宣言するときに型が宣言されます。

var pi float64 = 3.14
var week int = 7

これらの変数は、異なる方法で宣言した場合、異なるデータ型になる可能性があります。

これは、データ型が値に関連付けられているPHPなどの言語とは異なります。

$s = "sammy";         // $s is automatically a string
$s = 123;             // $s is automatically an integer

上記のコードブロックでは、最初の$sは値"sammy"が割り当てられているため文字列であり、2番目は値123があるため整数です。

次に、配列などのより複雑なデータ型を見てみましょう。

配列

arrayは、要素の順序付けられたシーケンスです。 アレイの容量は作成時に定義されます。 配列にサイズが割り当てられると、サイズは変更できなくなります。 配列のサイズは静的であるため、メモリを一度しか割り当てないことを意味します。 これにより、配列の処理が多少厳しくなりますが、プログラムのパフォーマンスが向上します。 このため、プログラムを最適化する際に通常配列が使用されます。 次に説明するSlicesは、より柔軟性があり、他の言語の配列と考えるものを構成します。

配列は、配列のサイズを宣言し、次に中括弧{ }で定義された値を持つデータ型を宣言することによって定義されます。

文字列の配列は次のようになります。

[3]string{"blue coral", "staghorn coral", "pillar coral"}

変数に配列を保存して出力できます。

coral := [3]string{"blue coral", "staghorn coral", "pillar coral"}
fmt.Println(coral)
Output[blue coral staghorn coral pillar coral]

前述のように、スライスは配列に似ていますが、はるかに柔軟です。 この可変データ型を見てみましょう。

スライス数

sliceは、長さが変化する可能性のある要素の順序付けられたシーケンスです。 スライスはサイズを動的に増やすことができます。 スライスに新しいアイテムを追加するときに、スライスに新しいアイテムを保存するのに十分なメモリがない場合、必要に応じてシステムにさらにメモリを要求します。 スライスは、必要に応じて要素を追加するために拡張できるため、配列よりも一般的に使用されます。

スライスは、開始および終了の角括弧[]が前に付いたデータ型を宣言し、中括弧{ }の間に値を持つことによって定義されます。

整数のスライスは次のようになります。

[]int{-3, -2, -1, 0, 1, 2, 3}

フロートのスライスは次のようになります。

[]float64{3.14, 9.23, 111.11, 312.12, 1.05}

文字列のスライスは次のようになります。

[]string{"shark", "cuttlefish", "squid", "mantis shrimp"}

文字列のスライスをseaCreaturesとして定義しましょう。

seaCreatures := []string{"shark", "cuttlefish", "squid", "mantis shrimp"}

変数を呼び出すことで出力できます:

fmt.Println(seaCreatures)

出力は、作成したリストとまったく同じになります。

Output[shark cuttlefish squid mantis shrimp]

appendキーワードを使用して、スライスにアイテムを追加できます。 次のコマンドは、seahorseの文字列値をスライスに追加します。

seaCreatures = append(seaCreatures, "seahorse")

印刷して追加したことを確認できます。

fmt.Println(seaCreatures)
Output[shark cuttlefish squid mantis shrimp seahorse]

ご覧のとおり、未知のサイズの要素を管理する必要がある場合、スライスは配列よりも汎用性が高くなります。

Maps

mapは、Goの組み込みハッシュまたは辞書タイプです。 マップは、keysvaluesをペアとして使用してデータを格納します。 これは、インデックス、またはこの場合はキーによって値をすばやく検索するプログラミングで役立ちます。 たとえば、ユーザーIDでインデックス付けされたユーザーのマップを保持することができます。 キーはユーザーIDであり、ユーザーオブジェクトは値です。 マップは、キーワードmap、角括弧[ ]のキーデータ型、中括弧内の値データ型とキー値のペアを使用して作成されます。

map[key]value{}

通常、IDに含まれる情報など、関連するデータを保持するために使用され、マップは次のようになります。

map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}

中括弧に加えて、マップ全体にコロンもあることに気付くでしょう。 コロンの左側の単語がキーです。 キーは、Goの任意のcomparableタイプにすることができます。 比較可能な型は、stringsintsなどのプリミティブ型です。 プリミティブ型は言語によって定義され、他の型の組み合わせからは構築されません。 ユーザー定義型にすることもできますが、プログラミングエラーを避けるためにシンプルに保つことをお勧めします。 上記の辞書のキーは、nameanimalcolor、およびlocationです。

コロンの右側の単語は値です。 値は任意のデータ型で構成できます。 上記の辞書の値は、Sammysharkblue、およびoceanです。

マップを変数内に保存して印刷します。

sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
fmt.Println(sammy)
Outputmap[animal:shark color:blue location:ocean name:Sammy]

サミーの色を分離したい場合は、sammy["color"]を呼び出すことで分離できます。 それを印刷してみましょう:

fmt.Println(sammy["color"])
Outputblue

マップはデータを保存するためのキーと値のペアを提供するため、Goプログラムの重要な要素になります。

結論

この時点で、Goで使用できる主要なデータ型のいくつかをよりよく理解する必要があります。 これらの各データ型は、Go言語でプログラミングプロジェクトを開発するときに重要になります。

Goで使用できるデータ型をしっかりと把握したら、状況に応じてデータ型を変更するためにHow To Convert Data Typesを学習できます。