Goは、スケーラブルで高性能なソフトウェアアプリケーションの開発に広く使用されている、人気のあるプログラミング言語です。Goの主な特徴の1つに、コードを整理して再利用しやすくするためのパッケージのサポートがあります。
Goのパッケージは、関連するGoソースファイルのコレクションで、ディレクトリ内にまとめられています。パッケージ内のファイルには通常、関連する関数、データ構造、型が含まれ、それらが連携して特定のタスクやタスクのセットを実行します。
Goのパッケージはコードの再利用とモジュール化を促進するように設計されており、開発者は再利用可能なコードを書いて、簡単にインポートして他のプロジェクトで使用することができます。
Goでパッケージを使用するには、importキーワードを使用してソースコードにインポートするだけです。パッケージをインポートすると、そのパッケージの関数、型、変数を自分のコードで使用することができます。
package <packageName>
新しいGoプログラムを作成するときは、通常、アプリケーションのエントリポイントであるメインパッケージを定義することから始めます。このパッケージには、プログラムの実行開始時に自動的に実行される特別なメイン関数が含まれています。
package main
Note: mainパッケージは、Goのプログラムが実行される起点となるような特別なパッケージを指します。mainパッケージの中には必ずmain関数を含んでいる必要があり、Goのプログラムは最初にmain関数が実行されます。このmainパッケージは、開発スペースのルートディレクトリにすることが一般的ですが、任意のディレクトリをmainパッケージとして指定することも可能です。
例えば、fmtパッケージを使用して、コンソールに出力したい場合、次のようにインポートします。
import "fmt"
fmtパッケージをimportした後は、パッケージが提供する関数を次のようにコード内で使用することができます。
fmt.Println("Hello, World!")
Goのfmtパッケージは、入出力の書式設定に使用されます。テキストやデータを出力するためにフォーマットしたり、入力を読み込んだりスキャンしたりするために使用できるいくつかの関数が提供されています。ここでは、fmtパッケージで最もよく使われる3つの関数、fmt.Println、fmt.Printf、fmt.Scanについて、それぞれの違いを説明します。
この関数は、標準出力に出力を表示するために使用されます。0個以上の引数を取り、出力の最後に改行文字を追加します。例えば、次のコードは文字列"Hello, world!"を表示し、その後に改行文字を追加します。
fmt.Println("Hello, world!")
この関数は、書式文字列を使用して標準出力をフォーマットするために使用されます。最初の引数に書式文字列を取り、その後に書式文字列の書式指定子に対応する0個以上の引数を取ります。フォーマット指定子は、引数がどのようにフォーマットされるべきかを指定するために使用されます。
例えば、次のコードは、文字列"Hello, world!"の後に改行文字を表示します。ここで、%sフォーマット指定子は、文字列"world"を挿入するために使用されます。
fmt.Printf("Hello, %s!\n", "world")
この関数は、コンソールからの入力を読み取るために使用されます。入力を格納する変数へのポインタを1つ以上取ります。string型のポインタを引数に代入することで、標準入力で入力された値をその変数で受け取ることができます。
var name string
fmt.Scan(&name)
mathパッケージは、基本的な算術演算、三角関数、対数など、さまざまな数学に関連した関数や定数を提供します。ここでは、mathパッケージに含まれる関数のいくつかを例に挙げて説明します。
package main
import (
"fmt"
"math"
)
func main() {
// 平方根を計算する
sqrt := math.Sqrt(16)
fmt.Println(sqrt) // 4
// 定数πを出力する
pi := math.Pi
fmt.Println(pi) // 3.141592653589793
// ラジアン単位の正弦を計算する
sin := math.Sin(math.Pi / 2)
fmt.Println(sin) // 1
}
timeパッケージは、日付と時刻を扱うための機能を提供します。日付と時刻をパース、フォーマット、操作するためのさまざまな関数が提供されています。以下は、Goのtimeパッケージの使用方法の例です。
現在の時刻を取得するには、timeパッケージのNow()関数を使用します。Now()関数は、現在の時刻を表すTimeオブジェクトを返します。
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
fmt.Println("Current Time:", t)
}
Timeオブジェクトを特定の方法でフォーマットするには、timeパッケージのFormat()関数を使用します。Format()関数は、レイアウト文字列を引数として受け取り、フォーマットされた文字列を返します。
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
fmt.Println("Formatted Time:", t.Format("02 Jan 2006 15:04:05"))
}
文字列を解析してTimeオブジェクトに変換するには、timeパッケージのParse()関数を使用します。Parse()関数は、レイアウト文字列とパースする文字列を受け取り、Timeオブジェクトを返します。
Note: Goのレイアウト文字列は"Jan 02 15:04:05 2006 MTS"というような形式になっており、左から順番に下のような意味を持ちます。
- 月(01のような数字表記でも良い)
- 日
- 時(15だと24時間表記)
- 分
- 秒
- 年
- タイムゾーン
例えば、"02 Jan 2006"は"dd MMM yyyy"というフォーマットで日付を表します。同様に、"15:04:05"は"hh:mm:ss"というフォーマットで時刻を表します。
t, _ := time.Parse("02 Jan 2006", "30 Jul 2021")
fmt.Println("Parsed Time:", t)
"02 Jan 2006"は入力文字列の期待される書式を指定するレイアウト文字列です。このレイアウト文字列を使って、入力文字列"30 Jul 2021"をどのように解釈するかをGoに伝えます。
Parse()関数はTimeオブジェクトを返し、変数tに格納します。レイアウトと入力文字列が正しいことが分かっているので、アンダースコア(_)を使ってParse()関数が返すエラー値を無視するようにしています。
Timeオブジェクトに時間を足したり引いたりするには、timeパッケージのAdd()関数やAddDate()関数を使用します。Add()関数は、Timeオブジェクトに継続時間を追加し、新しいTimeオブジェクトを返します。AddDate()関数は加算したい年、月、日を指定し、それに従って計算された新しいTimeオブジェクトを返します。もし、日付が範囲外の存在しない値になった場合は、Goはその値を存在する日付に自動的に変換します。
また、時間の引き算を行うメソッドは用意されていないため、日付の減算を行う場合はAdd()関数やAddDate()関数にマイナスの値を渡します。ここではAdd()メソッドを使って足し算と引き算を行なってみましょう。
t := time.Now()
fmt.Println("Current Time:", t)
future := t.Add(10 * time.Minute)
fmt.Println("Future Time:", future)
past := t.Add(-10 * time.Minute)
fmt.Println("Past Time:", past)
randパッケージは、乱数を生成するための機能を提供します。整数、浮動小数点数、バイトなど、さまざまな型の乱数を生成するための関数が用意されています。
特定の範囲の整数を生成するには、randパッケージのRand構造体のIntn()メソッドを使います。Intn()メソッドは引数として整数nを受け取り、0からn-1の間のランダムな整数を返します。
package main
import (
"fmt"
"math/rand"
)
func main() {
// シードの値が設定された疑似乱数生成機を用意
randomSource := rand.New(rand.NewSource(time.Now().UnixNano()))
// 0から9の間のランダムな整数を生成します
randomNum := randomSource.Intn(10)
fmt.Println("Random Number:", randomNum)
}
スライスからランダムな要素を抽出してみましょう。
randomSource := rand.New(rand.NewSource(time.Now().UnixNano()))
fruit := []string{"apple", "banana", "orange", "grape", "kiwi"}
randomFruit := fruit[randomSource.Intn(len(fruit))]
fmt.Println(randomFruit)
特定の範囲のランダムな浮動小数点数を生成するには、randパッケージのRand構造体のFloat64()メソッドを使用します。Float64()メソッドは0.0から1.0の間のランダムな浮動小数点数を返します。
// シードの値が設定された疑似乱数生成機を用意
randomSource := rand.New(rand.NewSource(time.Now().UnixNano()))
// -1.0から1.0の間のランダムな浮動小数点数を生成します
randomNum := randomSource.Float64()*2 - 1
fmt.Println("Random Number:", randomNum)
Rand構造体のShuffleメソッドはスライスの要素をランダムな順序でシャッフルします。Shuffleメソッドは、長さnとswap関数の2つの引数を取ります。swap関数はiとjという2つの整数の引数を取り、スライス内のiとjの位置の要素を入れ替えます。
// シードの値が設定された疑似乱数生成機を用意
randomSource := rand.New(rand.NewSource(time.Now().UnixNano()))
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
randomSource.Shuffle(len(numbers), func(i, j int) {
numbers[i], numbers[j] = numbers[j], numbers[i]
})
この例では10個の要素を持つ整数のスライスを定義します。そして、Shuffleメソッドでスライスの要素をシャッフルしています。len(numbers)は、Shuffleメソッドにスライスの長さを伝え、2番目の引数はswap関数で、スライス内のiとjの位置の要素を交換します。
関数、型、変数などのすべての識別子は、パブリックにするか、プライベートにするかのどちらかになります。
パブリック化にする場合、識別子は大文字で始め、プライベート化にする場合、識別子は小文字で始めます。パブリック化された識別子は他のパッケージのコードからアクセスできますが、プライベート化された識別子は定義されたパッケージ内でのみアクセスできます。
package mypackage
func MyPublicFunction() {
// ...
}
func myPrivateFunction() {
// ...
}
このパッケージでは、MyPublicFunctionは大文字で始まるのでパブリック化された関数、myPrivateFunctionは小文字で始まるのでプライベート化された関数となります。
エクスポートされた識別子は他のパッケージからアクセスできますが、エクスポートされていない識別子は、その識別子が定義されたパッケージ内でのみアクセスできます。これはGoの意図的な設計上の決定で、カプセル化を強制してパッケージの整合性を維持するのに役立っています。
たとえば、別のGoプログラムでmypackageパッケージをインポートする場合、MyPublicFunctionにはアクセスできますが、myPrivateFunctionにはアクセスできません。他のパッケージからエクスポートされていない識別子にアクセスしようとすると、コンパイルエラーになります。
package main
import "mypackage"
func main() {
mypackage.MyFunction() // ok
mypackage.myPrivateFunction() // error
}
関数だけでなく、型や変数も同様の操作を行うことができます。
package mypackage
type MyExportedType struct {
// ...
}
type myUnexportedType struct {
// ...
}
var MyExportedVariable int = 42
var myUnexportedVariable int = 24
このパッケージでは、MyExportedTypeとMyExportedVariableは共にエクスポートされ、myUnexportedTypeとmyUnexportedVariableは共にエクスポートされません。