【Goで学ぶコンピューターサイエンス】プログラミングの基礎

## プログラミングの基本要素

### **1. 変数とデータ型**

変数はプログラム中でデータを一時的に格納するための箱です。
Goでは静的型付けを採用しており、変数の型を明確に指定するか、型推論を利用します。
これにより、プログラムの型安全性が保証されます。

- **型の宣言**：`int`, `float64`, `string`, `bool` など、Goには基本的なデータ型が用意されています
- **型推論**：`:=` 演算子を使用すると、初期値に基づいて型を自動的に推論します
- **ゼロ値**：未初期化の変数は、型に応じたゼロ値（例えば、`int` 型なら `0`、`string` 型なら空文字列）を持ちます

#### **サンプルコード**
```go
package main

import "fmt"

func main() {
    // 明示的な型指定
    var age int = 30
    var height float64 = 1.75
    var name string = "Taro"

    // 型推論による宣言
    isDeveloper := true

    // ゼロ値
    var uninitialized int

    // 出力
    fmt.Printf("名前: %s\n", name)
    fmt.Printf("年齢: %d 歳\n", age)
    fmt.Printf("身長: %.2f メートル\n", height)
    fmt.Printf("開発者: %t\n", isDeveloper)
    fmt.Printf("未初期化の整数: %d\n", uninitialized)
}
```

### **2. 定数**

定数は、プログラム中で値が変更されない固定値を表します。例えば、物理定数やアプリケーション設定などに利用されます。

- **定義方法**：`const` キーワードを使用して宣言します
- **複数定義**：`const` ブロックを使用して複数の定数を一度に定義できます
- **iota**：連続する整数値を生成する場合に便利な特殊な識別子

#### **サンプルコード**
```go
package main

import "fmt"

const (
    Pi       = 3.14159
    AppName  = "GoApp"
    MaxUsers = 1000
    // iota を使った連続値
    First  = iota // 0
    Second        // 1
    Third         // 2
)

func main() {
    fmt.Println("アプリケーション名:", AppName)
    fmt.Printf("円周率: %.5f\n", Pi)
    fmt.Printf("最大ユーザー数: %d\n", MaxUsers)
    fmt.Printf("連続する値: %d, %d, %d\n", First, Second, Third)
}
```

### **3. 制御構文**

制御構文を用いることで、条件に応じた処理や繰り返し処理を記述できます。
Goの制御構文は、明確で読みやすいコードを書くために設計されています。

- **if文**：条件式が `true` の場合に処理を実行します。括弧 `()` は不要です
- **switch文**：複数の条件分岐を簡潔に記述できます。case 節に複数の値を指定可能です
- **forループ**：Goでは唯一のループ構文であり、`while` や `do-while` を含む全てのパターンを表現可能です

#### **if文の例**
```go
package main

import "fmt"

func main() {
    score := 85

    if score >= 90 {
        fmt.Println("優秀な成績です。")
    } else if score >= 70 {
        fmt.Println("良い成績です。")
    } else {
        fmt.Println("もっと努力しましょう。")
    }
}
```

#### **switch文の例**
```go
package main

import "fmt"

func main() {
    day := "金曜日"

    switch day {
    case "月曜日":
        fmt.Println("週の始まりです。")
    case "金曜日":
        fmt.Println("お疲れ様です。週末が近いです！")
    case "土曜日", "日曜日":
        fmt.Println("週末を楽しみましょう！")
    default:
        fmt.Println("平日を乗り切りましょう！")
    }
}
```

#### **forループの例**
```go
package main

import "fmt"

func main() {
    // カウントアップ
    for i := 1; i <= 5; i++ {
        fmt.Println("カウント:", i)
    }

    // スライスを利用したループ
    numbers := []int{10, 20, 30, 40}
    for index, value := range numbers {
        fmt.Printf("Index: %d, Value: %d\n", index, value)
    }
}
```

### **4. 関数**

関数は、コードの再利用性を高めるための基本単位です。
Goでは、引数や戻り値の型を明確に指定し、シンプルで明確な関数を定義できます。

- **単一戻り値**：戻り値の型を一つ指定します
- **複数戻り値**：Goの特徴的な機能で、エラー処理などに頻繁に使用されます
- **可変長引数**：複数の引数を受け取る場合に便利です

#### **単一戻り値の例**
```go
package main

import "fmt"

func double(x int) int {
    return x * 2
}

func main() {
    result := double(10)
    fmt.Printf("10の2倍: %d\n", result)
}
```

#### **複数戻り値の例**
```go
package main

import "fmt"

// 商と余りを返す関数
func divide(a, b int) (int, int) {
    if b == 0 {
        return 0, 0 // エラーハンドリングを省略
    }
    return a / b, a % b
}

func main() {
    quotient, remainder := divide(10, 3)
    fmt.Printf("商: %d, 余り: %d\n", quotient, remainder)
}
```

#### **可変長引数の例**
```go
package main

import "fmt"

// 数値を合計する関数
func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

func main() {
    result := sum(1, 2, 3, 4, 5)
    fmt.Printf("合計: %d\n", result)
}
```

### **5. データ構造**

Goでは、基本的なデータ構造として配列、スライス、マップ、構造体があります。
それぞれの特性を理解し、適切に使い分けることで効率的なプログラムを構築できます。

#### **配列**

配列は固定長のデータ構造で、同じ型のデータを連続して格納します。長さは宣言時に指定し、変更できません。

- 固定長のため、容量を超える要素を追加できません
- 要素にランダムアクセスが可能で高速です

```go
package main

import "fmt"

func main() {
    var arr [5]int // 長さ5の配列
    arr[0] = 10
    arr[1] = 20

    fmt.Println("配列:", arr)
    fmt.Println("配列の長さ:", len(arr))
}
```

#### **スライス**

スライスは可変長のデータ構造で、配列を基に動的なサイズ変更が可能です。

- 配列の一部を参照することで生成されます
- `append` 関数で要素を動的に追加可能です

```go
package main

import "fmt"

func main() {
    slice := []int{10, 20, 30}
    slice = append(slice, 40) // 要素を追加

    fmt.Println("スライス:", slice)
    fmt.Println("長さ:", len(slice))
    fmt.Println("容量:", cap(slice))
}
```

#### **マップ**

マップはキーと値のペアを格納するデータ構造で、Goのハッシュテーブルに相当します。

- キーは一意で、値にアクセスする際に高速です
- キーの型は比較可能である必要があります（例: `int`, `string`）

```go
package main

import "fmt"

func main() {
    scores := make(map[string]int) // マップの作成
    scores["Alice"] = 90
    scores["Bob"] = 85

    fmt.Println("スコア:", scores)
    fmt.Println("Aliceのスコア:", scores["Alice"])
}
```

#### **構造体**

構造体は複数のフィールドを持つカスタムデータ型を定義するために使用されます。

- データをグループ化して表現します
- メソッドを持たせることが可能です

```go
package main

import "fmt"

// ユーザー構造体
type User struct {
    Name  string
    Age   int
    Email string
}

func main() {
    user := User{"Taro", 25, "taro@example.com"}
    fmt.Printf("名前: %s, 年齢: %d, メール: %s\n", user.Name, user.Age, user.Email)
}
```

### **6. エラーハンドリング**

Goはエラーハンドリングに例外ではなく、明示的なエラー値を使用します。この設計により、エラーが発生する箇所を容易に追跡できます。

#### **基本的なエラーハンドリング**

**`error` 型**はGoの組み込みインターフェースで、エラーメッセージを表現します。

```go
package main

import (
    "errors"
    "fmt"
)

// エラーを返す関数
func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("ゼロで割ることはできません")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 0)
    if err != nil {
        fmt.Println("エラー:", err)
    } else {
        fmt.Println("結果:", result)
    }
}
```

### **7. インターフェース**

インターフェースは、Goの型システムにおいて非常に重要な役割を果たします。
特定のメソッドを実装することで、異なる型に共通の動作を持たせることができます。

#### **基本的なインターフェース**

```go
package main

import "fmt"

// インターフェースの定義
type Shape interface {
	Area() float64
}

// 構造体とメソッドの定義
type Circle struct {
	Radius float64
}

func (c Circle) Area() float64 {
	return 3.14 * c.Radius * c.Radius
}

type Rectangle struct {
	Width  float64
	Height float64
}

func (r Rectangle) Area() float64 {
	return r.Width * r.Height
}

func main() {
	var s Shape = Circle{Radius: 5}
	fmt.Println("Circle面積:", s.Area())

	s = Rectangle{Width: 4, Height: 5}
	fmt.Println("Rectangle面積:", s.Area())
}
```

### **8. 標準ライブラリの活用**

Goの標準ライブラリは非常に充実しており、多くのタスクを簡単に実現できます。
ここでは、特によく使われるパッケージとその使用例をいくつか紹介します。

#### **時間操作: `time` パッケージ**

`time` パッケージは、日付や時刻の操作を簡単に行うための機能を提供します。

**現在時刻を取得**

```go
package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    fmt.Println("現在時刻:", now)
}
```

**時間の計算**

```go
package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    oneWeekLater := now.Add(7 * 24 * time.Hour) // 1週間後
    fmt.Println("現在:", now)
    fmt.Println("1週間後:", oneWeekLater)
}
```

**時刻のフォーマット**

Goの時刻フォーマットは独特で、特定の日付（`2006-01-02 15:04:05`）を基に指定します。

```go
package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    fmt.Println("標準形式:", now.Format("2006-01-02 15:04:05"))
    fmt.Println("カスタム形式:", now.Format("2006/01/02 03:04PM"))
}
```

#### **文字列操作: `strings` パッケージ**

`strings` パッケージは、文字列操作に便利な関数を提供します。

**文字列の分割**

```go
package main

import (
    "fmt"
    "strings"
)

func main() {
    sentence := "Go is a powerful language"
    words := strings.Split(sentence, " ")
    fmt.Println("単語リスト:", words)
}
```

**文字列の結合**

```go
package main

import (
    "fmt"
    "strings"
)

func main() {
    words := []string{"Go", "is", "fun"}
    sentence := strings.Join(words, " ")
    fmt.Println("結合結果:", sentence)
}
```

**部分文字列の検索**

```go
package main

import (
    "fmt"
    "strings"
)

func main() {
    sentence := "Go is great for web development"
    contains := strings.Contains(sentence, "web")
    fmt.Println("含まれているか:", contains)
}
```

#### **入出力操作: `io` と `os` パッケージ**

`io` と `os` パッケージは、ファイルや標準入出力を操作する機能を提供します。

**ファイルの作成と書き込み**

```go
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Create("example.txt")
    if err != nil {
        fmt.Println("ファイル作成エラー:", err)
        return
    }
    defer file.Close()

    file.WriteString("Hello, Go!\n")
    fmt.Println("ファイルに書き込み完了")
}
```

**ファイルの読み込み**

```go
package main

import (
	"fmt"
	"os"
)

func main() {
	content, err := os.ReadFile("example.txt")
	if err != nil {
		fmt.Println("ファイル読み込みエラー:", err)
		return
	}
	fmt.Println("ファイル内容:")
	fmt.Println(string(content))
}
```

#### **JSON操作: `encoding/json` パッケージ**

`encoding/json` パッケージは、JSONデータのエンコードとデコードをサポートします。

**構造体からJSONに変換**

```go
package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email"`
}

func main() {
    user := User{Name: "Taro", Age: 30, Email: "taro@example.com"}
    jsonData, _ := json.Marshal(user)
    fmt.Println("JSONデータ:", string(jsonData))
}
```

**JSONから構造体に変換**

```go
package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email"`
}

func main() {
    jsonData := `{"name":"Taro","age":30,"email":"taro@example.com"}`
    var user User
    json.Unmarshal([]byte(jsonData), &user)
    fmt.Println("構造体:", user)
}
```

#### **乱数の生成: `math/rand` パッケージ**

`math/rand` パッケージを使うと、ランダムな数値を生成できます。

**ランダムな整数の生成**

```go
package main

import (
	"fmt"
	"math/rand"
)

func main() {
	fmt.Println("ランダム整数:", rand.Intn(100)) // 0～99
}
```

**ランダムな小数の生成**

```go
package main

import (
	"fmt"
	"math/rand"
)

func main() {
	fmt.Println("ランダム小数:", rand.Float64()) // 0.0～1.0
}
```