package main
import (
"context"
"fmt"
)
// 정수 제너레이터
func Range(ctx context.Context) <-chan int {
cho := make(chan int)
go func() {
defer close(cho)
for i := 2; ; i++ {
select {
case cho <- i:
case <-ctx.Done():
return
}
}
}()
return cho
}
// 배수 걸러주는 파이프
func FilterMultiples(n int) func(context.Context, <-chan int) <-chan int {
return func(ctx context.Context, chi <-chan int) <-chan int {
cho := make(chan int)
go func() {
defer close(cho)
for x := range chi {
if x%n == 0 {
continue
}
select {
case cho <- x:
case <-ctx.Done():
return
}
}
}()
return cho
}
}
func MakePrimes(ctx context.Context) <-chan int {
cho := make(chan int)
go func() {
defer close(cho)
chn := Range(ctx)
for {
select {
case n := <-chn:
chn = FilterMultiples(n)(ctx, chn) // 배수 걸러주는 파이프를 동적 연결
select {
case cho <- n:
case <-ctx.Done():
return
}
case <-ctx.Done():
return
}
}
}()
return cho
}
func PrintPrimes(max int) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for prime := range MakePrimes(ctx) {
if prime > max {
break
}
fmt.Println(prime)
}
}
func main() {
PrintPrimes(100)
}
제너레이터 -> 2
제너레이터 -> 2n 파이프 -> 3
제너레이터 -> 2n 파이프 -> 3n 파이프 -> 5
제너레이터 -> ... -> 소수 x
제너레이터 -> ... -> xn 파이프 -> 소수 y
y가 max 넘으면 context 이용해 종료
멋지지 않냐. 모 입문책에서 발췌.
사실 파이프라인이라는 건 Go만의 개념은 아니고 그냥 특정 필터나 모디파이어 기능을 하는 파이프를 쭉 연결해서 데이터를 쭉 통과시키는 거임 ㅇㅇ 여기서는 그 파이프라인을 만드는데 고루틴을 이용한 것. ㅇㅇ
그리고 그 파이프를 연결할 때 동적으로 연결했음.
<- 갸야ㅏ아아악