Go genericを使用して関数の小バージョンを実装しましょう


もしかしてNodejs、Python 3、Rustなどの言語を使ったことがありますか?
これらの言語にはmap,reduce,filterに代表される反復器に関連するAPIが存在する.
少し大げさに言えば、コンパイラは現代プログラミング言語資格証と呼ばれ、多くの言語でサポートされている機能です.
では、なぜみんなが小さなモーターを導入するのでしょうか.
次の文でコードを作ってみてください.
당신은 이름과, C 언어 시험점수, JAVA 언어 시험점수를 가지는 학생 배열을 가지고 있다.

여기서 학생 배열을 받아 학생들의 점수 평균을 측정한다

이후 평균이 90점 이상인 학생들만 가지고 정렬을 해야 한다.
goで作成すると、次のようになります.
package main

import (
	"fmt"
	"sort"
)

type Student struct {
	name         string
	cLanguage    float32
	javaLanguage float32
}
type StudentMean struct {
	name string
	mean float32
}
type ArrayStudentMean []StudentMean

func (a ArrayStudentMean) Len() int           { return len(a) }
func (a ArrayStudentMean) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ArrayStudentMean) Less(i, j int) bool { return a[i].mean < a[j].mean }

func main() {
	var students []Student = []Student{
		{name: "나", cLanguage: 100, javaLanguage: 100},
		{name: "철수", cLanguage: 90, javaLanguage: 80},
		{name: "영희", cLanguage: 90, javaLanguage: 95},
	}
	//
	var above90 = ArrayStudentMean(make([]StudentMean, 0, len(students)))
	for _, student := range students {
		var studentMean = StudentMean{
			name: student.name,
			mean: (student.cLanguage + student.javaLanguage) / 2,
		}
		if studentMean.mean >= 90 {
			above90 = append(above90, studentMean)
		}
	}
	sort.Sort(above90)
	//
	for _, data := range above90 {
		fmt.Printf("%s : %f\n", data.name, data.mean)
	}
}
わざと注釈をつけないで、上の説明とコードを見て、率直に考えてみましょう.
上のコードを見るだけで、このコードがどのようなアルゴリズムを実現しているかを直感的に知ることができますか?
これらのコードは使用に慣れているので、迅速に分析できますが、コードを見るだけでは文の役割が一度に分かるわけではありません.
また,上記のコードはGoの出生欠点sort.Interfaceに関係している.
GoはGenerickをサポートしていないため、ユーザ構造体をソートするには、上のコードにArrayStudentMeanと同じ配列タイプを作成し、LenSwapおよびLessメソッドを直接実装する必要がある.
ご覧のように、このコードは特にありませんが、ユーザーが直接実装する場合は、かなり面倒です.
特に最も問題があると考える部分はfor文の意味が不明確である.
私たちはすでに多くのfor文を見たことがあります.上のコードのfor文は実は簡単で、説明しにくいことはありませんが、これより長い条件があれば、このfor文がどんな目的で使われているのか分かりません.
では、上のコードと下のrustコードを比較してみましょう.
struct Student {
    name: String,
    cLanguage: f32,
    javaLanguage: f32,
}

fn main() {
    let students = vec![
        Student {
            name: "나".to_string(),
            cLanguage: 100.,
            javaLanguage: 100.,
        },
        Student {
            name: "철수".to_string(),
            cLanguage: 90.,
            javaLanguage: 80.,
        },
        Student {
            name: "영희".to_string(),
            cLanguage: 90.,
            javaLanguage: 95.,
        },
    ];
    let mut data = students
        .into_iter()
        .map(|student| {
            (
                student.name,
                (student.cLanguage + student.javaLanguage) / 2.,
            )
        })
        .filter(|(_, mean)| *mean >= 90.)
        .collect::<Vec<_>>();
    data.sort_by(|(_, a_mean), (_, b_mean)| a_mean.partial_cmp(b_mean).unwrap());
    data.iter().for_each(|(name, mean)| {
        println!("{} : {}", name, mean);
    });
}
ほとんどの人はrust言語を知らないかもしれませんが、上のソースコードを理解するのに問題はありません.
  • student配列mapにより平均を計算
  • filter90点以上のみ選択
  • collect平均90点以上
  • sort_byソート
  • for_each|を出力します.
  • コードの内容を小バージョンで読む前にその役割が理解できなかったfor文よりも、コードのみを使う意味が明確だと思います.
    そのため、私は小さなプログラム関連のAPIが大好きで、できればいつでもそれを使用します.
    したがって,実装されたイテレーションの使用は以下のようになる.
    type Student struct {
    	name         string
    	cLanguage    float32
    	javaLanguage float32
    }
    type StudentMean struct {
    	name string
    	mean float32
    }
    
    func main() {
    	var students []Student = []Student{
    		{name: "나", cLanguage: 100, javaLanguage: 100},
    		{name: "철수", cLanguage: 90, javaLanguage: 80},
    		{name: "영희", cLanguage: 90, javaLanguage: 95},
    	}
    
    	ForEach(func(t StudentMean) { fmt.Printf("%s : %f\n", t.name, t.mean) })(
    		Sorted(func(a, b StudentMean) int {
    			if a.mean == b.mean {
    				return 0
    			} else if a.mean < b.mean {
    				return -1
    			} else {
    				return 1
    			}
    		})(
    			Filter(func(t StudentMean) bool { return t.mean >= 90 })(
    				Map(func(t Student) StudentMean {
    					return StudentMean{name: t.name, mean: (t.cLanguage + t.javaLanguage) / 2}
    				})(
    					FromArray(students),
    				),
    			),
    		),
    	)
    }
    残念なことに、GoはMethodGenerickを許可せず、上記のような不便な小バージョンしか使用できません.
    ただし、今後、より先進的なGenericサポートとMethod Genericをサポートすれば、以下のコードを記述することができます.
    
    func main() {
    	var students []Student = []Student{
    		{name: "나", cLanguage: 100, javaLanguage: 100},
    		{name: "철수", cLanguage: 90, javaLanguage: 80},
    		{name: "영희", cLanguage: 90, javaLanguage: 95},
    	}
    	// 메서드 체이닝 방식의 이터레이터
    	FromArray(students)
    		.Map(func(t Student) StudentMean {
    			return StudentMean{name: t.name, mean: (t.cLanguage + t.javaLanguage) / 2}
    		})
    		.Filter(func(t StudentMean) bool { return t.mean >= 90 })
    		.Sorted(func(a, b StudentMean) int {
    			if a.mean == b.mean {
    				return 0
    			} else if a.mean < b.mean {
    				return -1
    			} else {
    				return 1
    			}
    		}),
    		.ForEach(func(t StudentMean) { fmt.Printf("%s : %f\n", t.name, t.mean) })
    }
    これからもっと活躍して、上のようにコードを書いたらどうなるのでしょうか.
    個人的には楽しみです.
    現在、ジュネーブの限界により、上記のコードは実現できません.
    正確には、.Mapの方法だけでは実現できない.(残りは実施可能)
    詳細については、go proposal : type-parameters #No-parameterized-methodsを参照してください.
    残念なことに、今回のジェニーンリックは私の期待を100%満たすことができなかった.
    しかし、その後もジュネーブの発展の可能性を非常に望んでいます.
    特に言語の観点からジェニーリックを支持し始めたのは希望だったが...goroutineをアプレットAPIに統合した場合??
    ウィジェットAPIは基本的に順序依存性がないため,goroutineによる並列処理成長が有望である.
    大股で進むGo言語乾杯!
    最後に、私が実現した奇形腫APIコードの最後の文章を完成します.
    // Lazy evaluation 기반
    type Iter[T any] func() (T, bool)
    
    // 배열을 Iterator 로 변환
    func FromArray[T any](array []T) Iter[T] {
    	var index = -1
    	var end T
    	return func() (T, bool) {
    		if index+1 < len(array) {
    			index += 1
    			return array[index], true
    		}
    		return end, false
    	}
    }
    
    // fn함수에 따라 매개변수로 주어지는 Iterator를 필터링
    func Filter[T any](fn func(T) bool) func(Iter[T]) Iter[T] {
    	return func(origin Iter[T]) Iter[T] {
    		var end T
    		return func() (T, bool) {
    			for data, ok := origin(); ok; data, ok = origin() {
    				if fn(data) {
    					return data, true
    				}
    			}
    			return end, false
    		}
    	}
    }
    
    // fn함수에 따라 매개변수로 주어지는 Iterator를 변환
    func Map[T any, E any](fn func(T) E) func(Iter[T]) Iter[E] {
    	return func(origin Iter[T]) Iter[E] {
    		var end E
    		return func() (E, bool) {
    			for data, ok := origin(); ok; data, ok = origin() {
    				return fn(data), true
    			}
    			return end, false
    		}
    	}
    }
    
    // fn함수에 따라 매개변수로 주어지는 Iterator를 정렬
    func Sorted[T any](fn func(a, b T) int) func(Iter[T]) Iter[T] {
    	return func(origin Iter[T]) Iter[T] {
    		var collect = Collect(origin)
    		var n = len(collect)
    		for i := 1; i < n; i++ {
    			j := i
    			for j > 0 {
    				if fn(collect[j-1], collect[j]) > 0 {
    					collect[j-1], collect[j] = collect[j], collect[j-1]
    				}
    				j = j - 1
    			}
    		}
    		return FromArray(collect)
    	}
    }
    
    // 매개변수로 주어지는 Iterator를 배열로 묶음
    func Collect[T any](origin Iter[T]) []T {
    	var collect = make([]T, 0, 10)
    	for data, ok := origin(); ok; data, ok = origin() {
    		collect = append(collect, data)
    	}
    	return collect
    }
    
    // Iterator각 요소를 fn을 적용시켜 실행
    func ForEach[T any](fn func(T)) func(Iter[T]) {
    	return func(origin Iter[T]) {
    		for data, ok := origin(); ok; data, ok = origin() {
    			fn(data)
    		}
    	}
    }