コンテンツにスキップ

プログラミング/Go

出典: フリー教科書『ウィキブックス(Wikibooks)』

Goの誕生と設計思想

[編集]

Goは、Googleの技術者であるRobert Griesemer、Rob Pike、Ken Thompsonによって2007年に開発が開始された、シンプルで効率的なプログラミング言語です。複雑さを排除し、読みやすさと生産性を重視する言語設計が特徴です。

基本的な文法と構造

[編集]

パッケージとインポート

[編集]

Goのプログラムは常にパッケージで構成され、実行可能なプログラムは必ずmainパッケージから始まります。

package main

import "fmt"

func main() {
    fmt.Println("Goの世界へようこそ!")
}

変数と型

[編集]
func variableExamples() {
    // 明示的な型宣言
    var name string = "田中"
    var age int = 30

    // 型推論
    city := "東京"
    
    // 複数の変数の宣言
    var x, y int = 10, 20

    // 構造体
    type Person struct {
        Name string
        Age  int
        City string
    }

    person := Person{
        Name: "山田",
        Age:  25,
        City: "大阪",
    }

    // マップ(辞書)
    scores := map[string]int{
        "数学": 90,
        "国語": 85,
        "英語": 88,
    }

    // スライス
    numbers := []int{1, 2, 3, 4, 5}
    filteredNumbers := numbers[1:4] // インデックス1から3までを抽出
}

関数と制御構造

[編集]
// 複数の戻り値を持つ関数
func calculateStats(numbers []int) (int, int, float64) {
    if len(numbers) == 0 {
        return 0, 0, 0.0
    }

    sum := 0
    for _, num := range numbers {
        sum += num
    }

    average := float64(sum) / float64(len(numbers))
    max := numbers[0]

    for _, num := range numbers[1:] {
        if num > max {
            max = num
        }
    }

    return sum, max, average
}

func main() {
    numbers := []int{10, 20, 30, 40, 50}
    total, maxValue, avg := calculateStats(numbers)

    // 条件分岐
    switch {
    case avg < 20:
        fmt.Println("平均値が低いです")
    case avg >= 20 && avg < 40:
        fmt.Println("平均値は標準的です")
    default:
        fmt.Println("平均値が高いです")
    }
}

並行処理とチャネル

[編集]

Goの並行処理は、goroutineとチャネルによって実現されます。

func worker(id int, jobs <-chan int, results chan<- int) {
    for job := range jobs {
        fmt.Printf("Worker %d processing job %d\n", id, job)
        time.Sleep(time.Second)
        results <- job * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    // ワーカーの起動
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // ジョブの送信
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    // 結果の収集
    for a := 1; a <= 5; a++ {
        fmt.Println(<-results)
    }

    // タイムアウト付きのチャネル操作
    select {
    case result := <-results:
        fmt.Println("結果:", result)
    case <-time.After(3 * time.Second):
        fmt.Println("タイムアウト")
    }
}

エラーハンドリング

[編集]
type CustomError struct {
    Message string
    Code    int
}

func (e *CustomError) Error() string {
    return fmt.Sprintf("エラー %d: %s", e.Code, e.Message)
}

func riskyOperation(value int) error {
    if value < 0 {
        return &CustomError{
            Message: "無効な値です",
            Code:    400,
        }
    }
    return nil
}

func main() {
    err := riskyOperation(-10)
    if err != nil {
        switch e := err.(type) {
        case *CustomError:
            fmt.Printf("カスタムエラー: %v\n", e)
        default:
            fmt.Println("不明なエラー")
        }
    }
}

インターフェースと構造的型付け

[編集]
type Shape interface {
    Area() float64
    Perimeter() float64
}

type Rectangle struct {
    Width  float64
    Height float64
}

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

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

func printShapeDetails(s Shape) {
    fmt.Printf("面積: %.2f\n周囲長: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    rect := Rectangle{Width: 5, Height: 3}
    printShapeDetails(rect)
}

ジェネリックス

[編集]

Go 1.18で導入されたジェネリックスは、型パラメータを使用して再利用可能なコードを書くことを可能にします。

package main

import (
	"fmt"
)

// 型制約の定義
type Ordered interface {
	int | int8 | int16 | int32 | int64 |
		uint | uint8 | uint16 | uint32 | uint64 |
		float32 | float64 | string
}

// ジェネリック関数:最大値を求める
func Max[T Ordered](a, b T) T {
	if a > b {
		return a
	}
	return b
}

// ジェネリック関数:スライスの最大値を求める
func MaxSlice[T Ordered](slice []T) T {
	if len(slice) == 0 {
		var zero T
		return zero
	}

	max := slice[0]
	for _, v := range slice[1:] {
		if v > max {
			max = v
		}
	}
	return max
}

// ジェネリック構造体:スタック
type Stack[T any] struct {
	items []T
}

func (s *Stack[T]) Push(item T) {
	s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() (T, bool) {
	if len(s.items) == 0 {
		var zero T
		return zero, false
	}

	index := len(s.items) - 1
	item := s.items[index]
	s.items = s.items[:index]
	return item, true
}

func (s *Stack[T]) IsEmpty() bool {
	return len(s.items) == 0
}

// ジェネリック関数:マップ操作
func MapSlice[T, U any](slice []T, fn func(T) U) []U {
	result := make([]U, len(slice))
	for i, v := range slice {
		result[i] = fn(v)
	}
	return result
}

// ジェネリック関数:フィルタリング
func Filter[T any](slice []T, predicate func(T) bool) []T {
	var result []T
	for _, v := range slice {
		if predicate(v) {
			result = append(result, v)
		}
	}
	return result
}

func main() {
	// 基本的な使用例
	fmt.Println("最大値(int):", Max(10, 20))
	fmt.Println("最大値(string):", Max("apple", "banana"))

	// スライスの最大値
	numbers := []int{1, 5, 3, 9, 2}
	fmt.Println("スライスの最大値:", MaxSlice(numbers))

	// スタックの使用例
	intStack := Stack[int]{}
	intStack.Push(10)
	intStack.Push(20)
	intStack.Push(30)

	for !intStack.IsEmpty() {
		value, ok := intStack.Pop()
		if ok {
			fmt.Println("スタックから取得:", value)
		}
	}

	// 文字列スタック
	stringStack := Stack[string]{}
	stringStack.Push("Hello")
	stringStack.Push("World")

	// マップ操作の例
	words := []string{"go", "rust", "python"}
	lengths := MapSlice(words, func(s string) int {
		return len(s)
	})
	fmt.Println("文字列の長さ:", lengths)

	// フィルタリングの例
	evenNumbers := Filter(numbers, func(n int) bool {
		return n%2 == 0
	})
	fmt.Println("偶数のみ:", evenNumbers)
}

Goの特徴と適用領域

[編集]

Goは以下のような分野で特に強みを発揮します:

  • クラウドインフラストラクチャ
  • マイクロサービス
  • ネットワークプログラミング
  • システムプログラミング
  • CLI(コマンドラインインターフェース)ツール

まとめ

[編集]

Goは、シンプルさ、高性能、並行処理のサポートを兼ね備えたプログラミング言語です。Google発のこの言語は、複雑さを排除し、開発者の生産性を最大化することを目指しています。初学者にも経験豊富な開発者にも、魅力的な選択肢となるでしょう。