JavaScript:関数を無限iterate して map して filter して take とかできるジェネレーターを考えてみた 3

[前の記事](https://qiita.com/ttatsf/items/d554277af612f7b6cc7a)の続きです。ここから読むとほとんど意味不明です。わからないところは適宜[戻って](https://qiita.com/ttatsf/items/d554277af612f7b6cc7a)読んでいただくか、コメントで質問していただけるとありがたいです。
ざっくり言うと、既に確定した配列にmapやfilterするのではなく、逆にまだ確定してない何かにmapやfilterしたいし、その結果を条件で区切って配列にしたい、という人(っていうかつまり自分)のための工夫です。

## こんな風に書けるようになったけど...
例によってFizzBuzzです。
[前の記事](https://qiita.com/ttatsf/items/d554277af612f7b6cc7a)ではパイプ風、関数合成風に書けるようにしてみました。


```Javascript:
const fizzBuzz = x =>
 x % 3 === 0 && x % 5 === 0 ? "FizzBuzz"
 : x % 3 === 0 ? "Fizz"
 : x % 5 === 0 ? "Buzz"
 : x
;

pipe(1)(nestG(n=>n+1), applyG(fizzBuzz), passTimesG(15), attachG(console.log), spreadG)
//あるいは
dot(spreadG, attachG(console.log), passTimesG(15), applyG(fizzBuzz), nestG(n=>n+1))(1)

//=>
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
```
## 実際使ってみると...
+ ジェネレーターと配列用の関数を混ぜて並べてしまってエラー出しまくり。
+ spreadGうざい。

混ぜて使えるようにしたのが、かえって混乱の元だったようで。

## こうなればいいのかな?
spreadGを関数内に隠蔽して

+ pipeG, dotG : ジェネレーター専用。ジェネレーターを合成し配列へ展開する
+ pipe, dot : 普通の関数を合成する
 
をわけてみました。

```Javascript:
const fizzBuzz = x =>
 x % 3 === 0 && x % 5 === 0 ? "FizzBuzz"
 : x % 3 === 0 ? "Fizz"
 : x % 5 === 0 ? "Buzz"
 : x
;

pipeG(1)(nestG(n=>n+1), applyG(fizzBuzz), passTimesG(15), attachG(console.log))
//あるいは
dotG(attachG(console.log), passTimesG(15), applyG(fizzBuzz), nestG(n=>n+1))(1)
```
ちょっとすっきり。

## これを使えばok：

```Javascript:
//関数fをくりかえしかけて、その結果を配列にします。スプレッド演算子が展開してくれるんで使いどころないかも。
function iterateG(f){
  return function*(xs){
    let yss = []
    let ys = xs
    while( true ) {
      yss = yss.concat( [ys] )
      yield yss
      ys = f( ys )
    }
  }
}
//関数fを繰り返しかけて結果を返します。[... ] と組み合わせてiterate相当。
function nestG( f ) {
  return function*( x ){
    while( true ) {
      yield x
      x = f( x )
    }
  }
}
// nestGの初期値を二つとる版。
const nest2G = f => x =>function*( y ){ 
  yield x 
  yield y 
  while(true){ 
    yield x = f(x)(y); 
    [x, y]= [y, x] 
  } 
}
//ジェネレーターgを呼び出し値に関数fをかけます。
//[... ] と組み合わせてmap相当。
function applyG( f ) {
  return function*( g ){
    let x = g.next();
    while( !x.done ){
      yield f( x.value );
      x = g.next();
    }
  }
}
// 内部値の初期値をaccとして、2引数関数fに内部値とgの値を適用し、結果を内部値とし、返す。
//reduce相当。[... ] と組み合わせてscan相当。
function accumG( f ){
  return function( acc ){
    return function*( g ){
      let x = g.next()
      let y = acc
      while( !x.done ){
        yield y = f( y, x.value )
        x = g.next()
      }
    }
  }
}
//gの値を判定関数fで調べ、真なら値を返します。偽なら値を返さず動作を継続。
//[... ] と組み合わせてfilter相当。
function passIfG( f ) {
  return function*( g ){
    let x = g.next();
    while( !x.done ) {
      if( f(x.value) ) yield x.value;
      x = g.next();
    }
  }
}
// gの値をn個ブロックします。[... ] と組み合わせてdrop相当。
function blockTimesG( n ){
  return function*( g ){
    let x 
    for(let i = 0; i < n; i++){
      x = g.next();
      if( x.done ) return;
    }
    x = g.next();
    while( !x.done ){
      yield x.value
      x = g.next();
    }
  }
}
//gの値をn個とりだします。とりだし終ったら終了メッセージを出す。
//[... ] と組み合わせてtake相当。
function passTimesG( n ){
  return function*( g ){
    let x
    for(let i = 0; i<n; i++){
      x = g.next();
      if( x.done ) return;
      yield x.value;
    }
  }
}
//gの値をfで調べ、真の間だけ値を返します。偽になったら終了メッセージを出す。
//[... ] と組み合わせてtakeWhile相当。
function passWhileG( f ){
  return function*( g ){
    let x = g.next()
    while( !x.done && f( x.value ) ) {
      yield x.value;
      x = g.next();
    }
  }
}
// gの値をfに適用して捨て、gの値をそのまま出力する。fの副作用が必要なとき使う。
function attachG( f ) {
  return function*( g ){
    let x= g.next();
    while( !x.done ) {
      f(x.value);
      yield x.value;
      x = g.next();
    }
  }
}

//関数合成のための関数
const pipe = x => (...f) => f.reduce( (acc, e) => e(acc), x )
const dot = (...f) => x => f.reduceRight( (acc, e) => e(acc), x )
//ジェネレーター専用の関数合成のための関数
const pipeG = x => (...f) =>{
  const spreadG = xs => [...xs]
  const fs =[...f, spreadG]
  return fs.reduce( (acc, e) => e(acc), x )
}
const dotG = (...f) => x => {
  const spreadG = xs => [...xs]
  const fs = [spreadG,...f]
  return fs.reduceRight( (acc, e) => e(acc), x )
}
//スプレッド構文、メソッドを関数にしておく
const spreadG = xs => [...xs]
const sortC = f => xs => xs.sort( f )
const mapC = f => xs => xs.map( f )
const filterC = f => xs => xs.filter( f )
const forEachC = f => xs => xs.forEach( f )
```

## 使用例：

```Javascript:
// 無限の等差数列から10個とる
pipeG(0)( nestG(n=>n+1), passTimesG(10) )
//=> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
// 普通の関数と合成。pipeGの結果をpipeの第一引数(初期値)としてセットします。
pipe( pipeG(0)(nestG(n=>n+1), passTimesG(10)) )
	( filterC(e=>e%2===0), forEachC(e=>console.log(e)) )
/*=>
0
2
4
6
8
*/
// pipeGの結果は配列なので、配列のメソッドが使えます。
pipeG(0)( nestG(n=>n+1),passTimesG(10) )
	.filter(e=>e%2===0).forEach(e=>console.log(e))
/*=>
0
2
4
6
8
*/
// 無限の等差数列から10個とる
dotG( passTimesG(10), nestG(n=>n+1) )(0)
//=> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
// 普通の関数と合成。dotGの結果をdotの第二引数(初期値)としてセットします。
dot( 
	forEachC(e=>console.log(e))
	, filterC(e=>e%2===0) 
)( dotG( passTimesG(10), nestG(n=>n+1) )(0) )
/*=>
0
2
4
6
8
*/
// 普通の関数と合成。dotGは第二引数を与えなければ普通の関数なのでdot内で使える。
dot( 
	forEachC(e=>console.log(e))
	, filterC(e=>e%2===0)
	, dotG( passTimesG(10), nestG(n=>n+1) )
)(0)
/*=>
0
2
4
6
8
*/
```

## ちょっと使ってみて...
あいかわらず、混ぜるな危険!であることには違いないのですが、ジェネレーター界と配列界を分けられるので、間違いに気がつくようにはなりました。

実際の使用例として、前に書いた記事をこれを使って書き直してみました。
[Javascript:Simulaの試着室シミュレーションをやってみる](https://qiita.com/ttatsf/items/c7c88d4a2eee160095dd)
[唐揚げつまんでみた](https://qiita.com/ttatsf/items/66680350eeb64956c207#追記最近の書きかたes6だとこんなかな)