コンテンツにスキップ

Go/メソッドチェイン

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

メソッドチェイン

[編集]

Goのメソッドは、メソッドチェイン(method chaining)が可能です。

メソッドチェイン
package main

import "fmt"

// Student 構造体を定義
type Student struct {
	name string
	age  int
}

// PrintName メソッドを定義(ポインタレシーバー)
func (s *Student) PrintName() *Student {
	fmt.Printf("Name: %v\n", s.name)
	return s // メソッドチェインのため、ポインタを返す
}

// PrintAge メソッドを定義(ポインタレシーバー)
func (s *Student) PrintAge() *Student {
	fmt.Printf("Age: %v\n", s.age)
	return s // メソッドチェインのため、ポインタを返す
}

func main() {
	// 学生のリストを定義
	students := [...]Student{
		{name: "joe", age: 17},
		{name: "tom", age: 18},
		{name: "alice", age: 17},
	}

	// 学生ごとにPrintNameとPrintAgeを呼び出し
	// 配列要素を直接ループ
	for _, student := range students {
		student.PrintName().PrintAge() // メソッドチェイン
	}
}
結果
Name: joe
Age: 17
Name: tom
Age: 18
Name: alice
Age: 17

遅延評価を使用するシーケンス

[編集]
lazy.go
package main

import (
	"fmt"
	"strings"
)

// Seq は遅延評価を使用するシーケンスを表す型
type Seq[T any] func(yield func(T) bool)

// 新しいSeqを作成する関数
func NewSeq[T any](elements ...T) Seq[T] {
	return func(yield func(T) bool) {
		for _, el := range elements {
			if !yield(el) {
				break
			}
		}
	}
}

// Map はSeqの各要素に対して関数を適用
func (seq Seq[T]) Map(fn func(T) T) Seq[T] {
	return func(yield func(T) bool) {
		seq(func(v T) bool {
			return yield(fn(v))
		})
	}
}

// Filter はSeqの各要素を条件でフィルタリング
func (seq Seq[T]) Filter(fn func(T) bool) Seq[T] {
	return func(yield func(T) bool) {
		seq(func(v T) bool {
			if fn(v) {
				return yield(v)
			}
			return true
		})
	}
}

// Reduce はSeqの各要素を累積的に処理
func (seq Seq[T]) Reduce(fn func(T, T) T, initial T) T {
	accumulator := initial
	seq(func(v T) bool {
		accumulator = fn(accumulator, v)
		return true
	})
	return accumulator
}

// Drop はSeqの先頭からn個の要素を削除
func (seq Seq[T]) Drop(n int) Seq[T] {
	return func(yield func(T) bool) {
		count := 0
		seq(func(v T) bool {
			if count < n {
				count++
				return true
			}
			return yield(v)
		})
	}
}

// Take はSeqの先頭からn個の要素を取得
func (seq Seq[T]) Take(n int) Seq[T] {
	return func(yield func(T) bool) {
		count := 0
		seq(func(v T) bool {
			if count < n {
				count++
				return yield(v)
			}
			return false
		})
	}
}

// Concat は二つのSeqを結合
func (seq1 Seq[T]) Concat(seq2 Seq[T]) Seq[T] {
	return func(yield func(T) bool) {
		seq1(yield)
		seq2(yield)
	}
}

// ForEach はSeqの各要素に対して指定された関数を実行
func (seq Seq[T]) ForEach(fn func(T)) {
	seq(func(v T) bool {
		fn(v)
		return true
	})
}

// ToSlice はSeqをスライスに変換(JSのtoArray()相当)
func ToSlice[T any](seq Seq[T]) []T {
	var result []T
	seq(func(v T) bool {
		result = append(result, v)
		return true
	})
	return result
}

// ToArray はSeqをスライスに変換(JSのtoArray()と同等)
func (seq Seq[T]) ToArray() []T {
	return ToSlice(seq)
}

// Every はすべての要素が条件を満たすか確認(JSのevery()相当)
func (seq Seq[T]) Every(fn func(T) bool) bool {
	result := true
	seq(func(v T) bool {
		if !fn(v) {
			result = false
			return false // 一つでも条件を満たさない要素があれば中断
		}
		return true
	})
	return result
}

// Some は少なくとも一つの要素が条件を満たすか確認(JSのsome()相当)
func (seq Seq[T]) Some(fn func(T) bool) bool {
	result := false
	seq(func(v T) bool {
		if fn(v) {
			result = true
			return false // 一つでも条件を満たす要素があれば中断
		}
		return true
	})
	return result
}

// Find は条件を満たす最初の要素を見つける(JSのfind()相当)
func (seq Seq[T]) Find(fn func(T) bool) (T, bool) {
	var found T
	foundAny := false

	seq(func(v T) bool {
		if fn(v) {
			found = v
			foundAny = true
			return false // 条件を満たす要素を見つけたら中断
		}
		return true
	})

	return found, foundAny
}

// FlatMap は各要素にマッピング関数を適用し、結果をフラット化(JSのflatMap()相当)
func (seq Seq[T]) FlatMap(fn func(T) Seq[T]) Seq[T] {
	return func(yield func(T) bool) {
		seq(func(v T) bool {
			innerSeq := fn(v)
			continueOuter := true

			innerSeq(func(innerV T) bool {
				if !yield(innerV) {
					continueOuter = false
					return false
				}
				return true
			})

			return continueOuter
		})
	}
}

// Stringerインターフェースを実装
func (seq Seq[T]) String() string {
	slice := []string{}
	seq(func(v T) bool {
		slice = append(slice, fmt.Sprintf("%v", any(v)))
		return true
	})
	return fmt.Sprintf("{%s}", strings.Join(slice, ", "))
}

// GoStringerインターフェースを実装
func (seq Seq[T]) GoString() string {
	slice := []string{}
	seq(func(v T) bool {
		slice = append(slice, fmt.Sprintf("%v", any(v)))
		return true
	})
	return fmt.Sprintf("NewSeq(%s)", strings.Join(slice, ", "))
}

func main() {
	// Seqを作成
	seq1 := NewSeq(1, 2, 3)
	seq2 := NewSeq(4, 5)

	// Seqを結合
	seq3 := seq1.Concat(seq2)

	// 結果をToSliceで表示
	fmt.Printf("Concatenated Seq: %s(%#v)\n", seq3, seq3) // {1, 2, 3, 4, 5}(NewSeq(1, 2, 3, 4, 5))

	// Mapを使って各要素を2倍にする
	doubledSeq := seq3.Map(func(x int) int {
		return x * 2
	})
	fmt.Println("Doubled Seq:", doubledSeq) // {2, 4, 6, 8, 10}

	// Filterを使って偶数を抽出
	evenSeq := seq3.Filter(func(x int) bool {
		return x%2 == 0
	})
	fmt.Println("Even Seq:", evenSeq) // {2, 4}

	// ForEachを使って各要素を表示
	seq3.ForEach(func(x int) {
		fmt.Println("ForEach element:", x)
	})

	// Reduceを使って合計を計算
	sum := seq3.Reduce(func(acc, x int) int {
		return acc + x
	}, 0)
	fmt.Println("Sum:", sum) // 15

	seq := NewSeq(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

	// Dropのテスト
	droppedSeq := seq.Drop(3)
	fmt.Println("Dropped Seq:", droppedSeq) // {4, 5, 6, 7, 8, 9, 10}

	// Takeのテスト
	takenSeq := seq.Take(5)
	fmt.Println("Taken Seq:", takenSeq) // {1, 2, 3, 4, 5}

	// DropとTakeの組み合わせ
	combinedSeq := seq.Drop(2).Take(3)
	fmt.Println("Combined Seq:", combinedSeq) // {3, 4, 5}

	// 新しく追加したメソッドのテスト

	// Everyのテスト
	allLessThan11 := seq.Every(func(x int) bool {
		return x < 11
	})
	allEven := seq.Every(func(x int) bool {
		return x%2 == 0
	})
	fmt.Println("All less than 11:", allLessThan11) // true
	fmt.Println("All even:", allEven)               // false

	// Someのテスト
	hasEven := seq.Some(func(x int) bool {
		return x%2 == 0
	})
	hasNegative := seq.Some(func(x int) bool {
		return x < 0
	})
	fmt.Println("Has even numbers:", hasEven)         // true
	fmt.Println("Has negative numbers:", hasNegative) // false

	// Findのテスト
	firstEven, foundEven := seq.Find(func(x int) bool {
		return x%2 == 0
	})
	firstBig, foundBig := seq.Find(func(x int) bool {
		return x > 100
	})
	fmt.Println("First even number:", firstEven, "Found:", foundEven)    // 2 true
	fmt.Printf("First number > 100: %v Found: %v\n", firstBig, foundBig) // 0 false

	// FlatMapのテスト
	poweredSeq := seq.Take(3).FlatMap(func(x int) Seq[int] {
		return NewSeq(x, x*x)
	})
	fmt.Println("FlatMapped powered sequence:", poweredSeq) // {1, 1, 2, 4, 3, 9}

	// ToArrayのテスト
	array := seq.Take(4).ToArray()
	fmt.Println("ToArray result:", array) // [1 2 3 4]
}

脚註

[編集]