シクシク素数列 Advent Calendar 2018 golang編


シクシク素数列 Advent Calendar 2018 の 22日目になります。


golangでの素数算出については、公式にgoroutineとchannelを利用した方式があります。
https://golang.org/doc/play/sieve.go

これを流用して、素数からシクシク素数を抜粋しました。
テストしやすいように、素数算出箇所はmain関数から取り出しています。

## プログラム
```golang:prime49.go
package main

import (
	"os"
	"strconv"
	"strings"
)

func Generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i
	}
}

func Filter(in <-chan int, out chan<- int, prime int) {
	for {
		i := <-in
		if i%prime != 0 {
			out <- i
		}
	}
}

func Execute(max int) string {
	list := []string{}
	ch := make(chan int)
	go Generate(ch)
	for i := 0; len(list) < max; i++ {
		prime := <-ch
		ch1 := make(chan int)
		go Filter(ch, ch1, prime)
		ch = ch1
		// シクシク素数列のみ記録
		if s := strconv.Itoa(prime); strings.ContainsAny(s, "4|9") {
			list = append(list, s)
		}
	}
	return strings.Join(list, ",")
}

func main() {
	max := 0
	if len(os.Args) > 1 {
		max, _ = strconv.Atoi(os.Args[1])
	}
	print(Execute(max), "\n")
}

```

## 出力例
```
$ go run prime49.go 9
19,29,41,43,47,59,79,89,97

$ go run prime49.go 10
19,29,41,43,47,59,79,89,97,109

$ go run prime49.go 100
19,29,41,43,47,59,79,89,97,109,139,149,179,191,193,197,199,229,239,241,269,293,347,349,359,379,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,509,541,547,569,593,599,619,641,643,647,659,691,709,719,739,743,769,797,809,829,839,859,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1019,1039,1049,1069,1091,1093,1097,1109,1129,1193,1229,1249,1259,1279,1289,1291,1297,1319
```

## テストコード
```golang:prime49_test.go
package main

import (
	"fmt"
	"testing"
)

func TestExecute(t *testing.T) {
	tests := []struct {
		num    int
		result string
	}{
		{0, ""},
		{1, "19"},
		{2, "19,29"},
		{3, "19,29,41"},
		{9, "19,29,41,43,47,59,79,89,97"},
		{10, "19,29,41,43,47,59,79,89,97,109"},
		{99, "19,29,41,43,47,59,79,89,97,109,139,149,179,191,193,197,199,229,239,241,269,293,347,349,359,379,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,509,541,547,569,593,599,619,641,643,647,659,691,709,719,739,743,769,797,809,829,839,859,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1019,1039,1049,1069,1091,1093,1097,1109,1129,1193,1229,1249,1259,1279,1289,1291,1297"},
		{100, "19,29,41,43,47,59,79,89,97,109,139,149,179,191,193,197,199,229,239,241,269,293,347,349,359,379,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,509,541,547,569,593,599,619,641,643,647,659,691,709,719,739,743,769,797,809,829,839,859,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1019,1039,1049,1069,1091,1093,1097,1109,1129,1193,1229,1249,1259,1279,1289,1291,1297,1319"},
		{101, "19,29,41,43,47,59,79,89,97,109,139,149,179,191,193,197,199,229,239,241,269,293,347,349,359,379,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,509,541,547,569,593,599,619,641,643,647,659,691,709,719,739,743,769,797,809,829,839,859,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1019,1039,1049,1069,1091,1093,1097,1109,1129,1193,1229,1249,1259,1279,1289,1291,1297,1319,1399"},
	}
	for _, tt := range tests {
		description := fmt.Sprintf("input:[%v]", tt.num)
		t.Run(description, func(t *testing.T) {
			msg := Execute(tt.num)
			if msg != tt.result {
				t.Fatal("出力値が異なる[", msg, "][", tt.result, "]")
			}
		})
	}
}
```

## 別プログラム

goroutine と channel を利用しない場合で書いてみました。
単に、構造体とメソッド、無名関数、flagを利用してみたかったというのもあります。
素数算出においては、あらかじめ「2」を素数として記録しておき、以降は奇数のみを確認対象として実施しています。シクシク素数の抽出は最初のバージョンと同じです。

```golang:prime49.go
package main

import (
    "fmt"
    "flag"
    "strconv"
    "strings"
)

type Prime4949 struct {
    PrimeNumbers [] int
    List49 [] string
}

func main() {
    var nCount = flag.Int("N", 9, "N=9")
    var nMax = flag.Int("MAX", 100, "MAX=100")
    var isMsgOpt = flag.Bool("v", false, "v=true")
    flag.Parse()
    if *nCount > *nMax {
        *nCount = * nMax
    }

    var pri Prime4949
    msg := pri.Execute(*nCount)
    if *isMsgOpt {
        msg = fmt.Sprintf("N=[%v] :", *nCount) + msg
    }
    fmt.Println(msg)
}

func (pri *Prime4949) Execute(nCount int) string {
    const firstPrimeNum = 2
    pri.PrimeNumbers = []int{firstPrimeNum}
    pri.List49 = []string{}
    for i := firstPrimeNum+1; len(pri.List49) < nCount; i += 2 {
       pri.addPrime(i)
    }
    msg := strings.Join(pri.List49 ,",")
    return msg
}

func (pri *Prime4949) addPrime(num int) {
    check := func () bool{
        if num < 2 {
            return false
        }
        isPrime := true
        for _, prime := range pri.PrimeNumbers {
            if num % prime == 0 {
                isPrime = false
                break
            }
        }
        return isPrime
    }
    if check() {
        pri.PrimeNumbers = append(pri.PrimeNumbers, num)
        if s := strconv.Itoa(num); strings.ContainsAny(s, "4 | 9") {
            pri.List49 = append(pri.List49, s)
        }
    }
}
```

## 出力例（別プログラム）

```
$ go run prime49.go -N 9
19,29,41,43,47,59,79,89,97

$ go run prime49.go -N 10
19,29,41,43,47,59,79,89,97,109

$ go run prime49.go -N 100
19,29,41,43,47,59,79,89,97,109,139,149,179,191,193,197,199,229,239,241,269,293,347,349,359,379,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,509,541,547,569,593,599,619,641,643,647,659,691,709,719,739,743,769,797,809,829,839,859,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1019,1039,1049,1069,1091,1093,1097,1109,1129,1193,1229,1249,1259,1279,1289,1291,1297,1319
```