title: パイプライン技術を使用した関数のストリーム呼び出しの実現
date: 2022-07-04 10:40:00
toc: false
index_img: http://api.btstu.cn/sjbz/?lx=m_dongman&cid=10
category:
- Go
tags: - 順序
- 接続
- サポート
- リクエスト
- メソッド
- 関数
パイプライン(Pipeline)という用語は、Unix のシェルコマンドから派生しており、単純なコマンドを組み合わせることで強力な機能を実現することができます。たとえば、システムのプロセスリストから nginx プロセスをフィルタリングしたい場合、次のようにします:
ps -ef | grep nginx
ここでは、ps と grep という 2 つの基本的な Unix コマンドをパイプで接続し、ps -ef の結果に対して grep nginx で nginx プロセスをフィルタリングしています。
関数型プログラミングでは、パイプラインの考え方を活用して、いくつかの単純な関数を連結してより強力な機能を構築することもできます。最も一般的なのは、ストリーム関数呼び出し(水のように、オブジェクト指向プログラミングではストリームインターフェースパターンに対応し、チェーン処理を実現できます)です。
これにより、各関数は自分が処理することに専念し、それを最大限に活用し、組み合わせ(パイプライン)によってより複雑なビジネス機能を構築することができます。これは、SOLID 設計原則の単一責任原則に合致しています。
私たちは、main 関数でMap-Reduce-Filter
機能モジュールをパイプラインで組み合わせ、これらの関数のストリーム呼び出しを実現しています:
package main
import (
"fmt"
"log"
)
type user struct {
name string
age int
}
func main() {
var users = []user{
{
name: "xmp",
age: 11,
},
{
name: "wlm",
age: 24,
},
{
name: "wjf",
age: 30,
},
{
name: "lm",
age: 50,
},
}
// プロセッサ関数は宣言された順序で順次呼び出されます
result := sumAge(users, filterAge, mapAgeToSlice)
fmt.Println("総年齢(40より大きく20より小さいものを除く):", result)
}
func filterAge(users []user) interface{} {
var result []user
for _, user := range users {
if user.age < 40 && user.age > 20 {
result = append(result, user)
}
}
return result
}
func mapAgeToSlice(users []user) interface{} {
var result []int
for _, user := range users {
result = append(result, user.age)
}
return result
}
func sumAge(users []user, pips ...func([]user) interface{}) (result int) {
var reusltSlice []int
for _, f := range pips {
runResult := f(users)
switch runResult.(type) {
case []user:
users = runResult.([]user)
case []int:
reusltSlice = runResult.([]int)
}
}
if len(reusltSlice) == 0 {
log.Fatalln("パイプラインにmapAgeToSliceメソッドが含まれていません")
}
for _, age := range reusltSlice {
result += age
}
return result
}
ここで、辞書型の代わりにユーザー構造体を導入してコードをより簡潔にし、可読性を向上させました。構造体型については、次の章である Go の型システムで詳しく説明します。
次に、Filter と Map 関数内のクロージャ関数を削除し、コード内で直接実装するように変更しました。これにより、コードを簡素化できます。また、パイプラインで Filter と Map 関数を一元的に宣言するために、それらの戻り値を空のインターフェース interface {} として宣言しました。
次に、重要なのは、Reduce 関数 sumAge の実装です。ここで、第 2 引数を可変長引数型として宣言し、複数の処理関数を渡すことができるようにしました。これらの処理関数は宣言された順序で順次呼び出されます。これらの処理関数の戻り値の型は空のインターフェースと宣言されているため、実行時にその戻り値の型を動的にチェックし、指定された変数に代入する必要があります。これにより、プログラムが期待するパスで実行され、型の問題によるエラーで終了することがありません。
パイプラインを使用することで、Filter->Map->Reduce のようなストリーム呼び出しをよりエレガントに実現することができます。さらに、パイプライン技術は HTTP リクエストのミドルウェア処理でも広く使用されています。