「javascript高級プログラム設計」学習ノート𞓜7.3.ジェネレータ


「先端の民謡」に注目し、より多くのオリジナル技術文章を読む.
生成器
  • ES 6に追加された構造は、1つの関数ブロック内でコードの実行を一時停止して復元することができ、カスタマイズしたディエテンダーと協働
  • を実現することができます.
    関連コード→
    ジェネレータベース
  • 生成器の形式は関数であり、関数名の前に星番号*
  • を追加する.
  • は、関数の場所を定義することができ、すべてジェネレータ(矢印関数を除く)
  • を定義することができます.
    function* generatorFn() {
         } //        
    let gfn = function* () {
         } //         
    let foo = {
         
      *generatorFn() {
         }, //               
    }
    class Foo {
         
      *generatorFn() {
         } //             
    }
    class FooStatic {
         
      static *generatorFn() {
         } //             
    }
    
  • ジェネレータ関数を呼び出すとジェネレータオブジェクトが生成され、生成器オブジェクトはIteratorインターフェースを実現し、next()方法
  • を有する.
    const g = generatorFn() //        ,       
    console.log(g) // generatorFn {},     
    console.log(g.next) //        next()  
    
  • next()方法の戻り値は、ドーン属性とvalue属性
  • と同様である.
  • 関数体が空のジェネレータから一度next()を呼び出すとdone:true状態
  • になります.
    console.log(g.next()) // { value: undefined, done: true },     
    
  • は、生成器関数の戻り値によりvalueの戻り値(デフォルトはundefined)
  • を指定することができる.
    function* generatorFn2() {
         
      return 'foo'
    }
    const g2 = generatorFn2()
    console.log(g2.next()) // { value: 'foo', done: true }
    console.log(g2.next()) // { value: undefined, done: true },  
    
  • ジェネレータ関数は、next()メソッドを初回起動した後だけ実行を開始します.
    function* generatorFn3() {
         
      console.log('         ')
    }
    const g3 = generatorFn3() //        ,       (         ,     )
    g3.next() // '         ',    next()  ,         ,    
    
  • 生成器オブジェクトは、自己参照
  • であるIterableインターフェースを実現する.
    function* generatorFn4() {
         }
    console.log(generatorFn4) // ƒ* generatorFn4() {},     
    
    const g4 = generatorFn4()
    console.log(g4) // generatorFn4 {},     
    
    console.log(g4[Symbol.iterator]) // ƒ [Symbol.iterator]() { [native code] },       
    console.log(g4[Symbol.iterator]()) // generatorFn4 {},   
    
    console.log(g4 === g4[Symbol.iterator]()) // true,                
    
    yieldによる実行中断
  • yieldキーワードは、生成器を停止させ、実行を開始させることができる.
  • 生成器がyieldキーワードに出会う前に正常に
  • を実行する.
  • yieldキーワードに遭遇してから実行を停止し、関数領域の状態は
  • に保持される.
  • 実行を停止したジェネレータ関数は、ジェネレータオブジェクト呼び出しnext()方法により
  • を再開する.
    function* generatorFn5() {
         
      yield
    }
    let g5 = generatorFn5()
    console.log(g5.next()) // { value: undefined, done: false },yield    
    console.log(g5.next()) // { value: undefined, done: true },        
    
  • yieldキーワードは、関数のreturn文と同様に機能し、その生成値はnext()方法で返されたオブジェクトに現れるが、doneの状態は異なる.
  • yieldキーワードで終了したジェネレータ関数はdone:false状態
  • にあります.
  • returnキーワードで終了したジェネレータ関数はdone:true状態
  • にあります.
    function* generatorFn6() {
         
      yield 'foo'
      yield 'bar'
      return 'baz'
    }
    let g6 = generatorFn6()
    console.log(g6.next()) // { value: 'foo', done: false },yield     
    console.log(g6.next()) // { value: 'bar', done: false },yield     
    console.log(g6.next()) // { value: 'baz', done: true },return     
    
  • 同一のジェネレータ関数の異なるジェネレータオブジェクト間には連絡がなく、一つのジェネレータオブジェクト上でnext()を呼び出す方法は他のジェネレータ
  • に影響を与えない.
    let g7 = generatorFn6() //      g7
    let g8 = generatorFn6() //      g8
    
    console.log(g7.next()) // { value: 'foo', done: false }
    console.log(g8.next()) // { value: 'foo', done: false }
    console.log(g8.next()) // { value: 'bar', done: false }
    console.log(g7.next()) // { value: 'bar', done: false }
    
  • yieldキーワードは、ジェネレータ関数の内部にある必要があります.直接にジェネレータ関数の定義に使用され、他の場所やネスト用の非ジェネレータ関数は
  • にエラーが発生します.
    function* validGeneratorFn() {
         
      yield 'result'
    }
    function* invalidGeneratorFnA() {
         
      function a() {
         
        yield 'result' // SyntaxError: Unexpected string
      }
    }
    function* invalidGeneratorFnB() {
         
      const b = () => {
         
        yield 'result' // SyntaxError: Unexpected string
      }
    }
    function* invalidGeneratorFnC() {
         
      ;(() => {
         
        yield 'result' // SyntaxError: Unexpected string
      })()
    }
    
    ジェネレータオブジェクトをイテレーションオブジェクトとして使用します.
  • は、生成対象を反復可能なオブジェクトとして扱う
  • .
    function* generatorFn7() {
         
      //      
      yield 1
      yield 2
      yield 3
    }
    for (const x of generatorFn7()) {
         
      //        generatorFn7,generatorFn7()      
      console.log(x)
      /* 
        1
        2
        3
      */
    }
    
  • 生成器を用いて反復サイクルの回数を制御する
  • function* nTimes(n) {
         
      while (n--) {
         
        console.log(n)
        yield
      }
    }
    for (let _ of nTimes(3)) {
         
      console.log(_)
      /* 
        2, 1   n
        undefined, 1   yield
        1, 2   n
        undefined, 2   yield
        0, 3   n
        undefined, 3   yield
      */
    }
    
    yieldを使って入力と出力を実現します.
  • は、関数としての中間戻り文に加えて、yieldキーワードを関数の中間パラメータとして使用することもできる.
  • 前回、ジェネレータ関数を一時停止させたyieldのキーワードは、next()方法に伝達された最初の値
  • を受信する.
  • 最初の呼び出しnext()から入力された値は、ジェネレータ関数
  • の実行を開始するためだけに使用されない.
    function* generatorFn8() {
         
      console.log(yield)
      console.log(yield)
      console.log(yield)
    }
    let g9 = generatorFn8() //        ,       
    g9.next('bar') //      next()       ,            
    g9.next('baz') // 'baz',  next()  baz,         yield  
    g9.next('qux') // 'qux',  next()  qux,         yield  
    
  • yieldキーワードを入力と出力に同時に使用する(returnキーワードと同時に使用する)
  • next()方法にはパラメータがありません.yieldキーワードに遭遇したら
  • を停止します.
  • next()方法にはパラメータがあり、同じyiieldに渡す値としてパラメータがあり、生成器関数実行returnは今回のエントリの値
  • に戻る.
    function* generatorFn9() {
         
      return yield 'foo'
    }
    let g10 = generatorFn9()
    console.log(g10.next()) // { value: 'foo', done: false },next()    ,  yield       ,        
    console.log(g10.next('bar')) // { value: 'bar', done: true },next()   ,         yield  ,   return 'bar'
    
  • yieldキーワードは何回も
  • を使用します.
    function* generatorFn10() {
         
      for (let i = 0; ; i++) {
         
        yield i
      }
    }
    let g11 = generatorFn10()
    console.log(g11.next()) // { value: 0, done: false }
    console.log(g11.next()) // { value: 1, done: false }
    console.log(g11.next()) // { value: 2, done: false }
    console.log(g11.next()) // { value: 3, done: false }
    
  • は、反復回数に応じて対応するインデックス
  • を生成する.
    function* nTimes(n) {
         
      let i = 0
      while (n--) {
         
        yield i++
      }
    }
    for (let x of nTimes(3)) {
         
      console.log(x)
      /* 
        0
        1
        2
      */
    }
    
  • 生成器を用いた実現範囲
  • function* range(start, end) {
         
      while (end > start) {
         
        yield start++
      }
    }
    for (const x of range(4, 7)) {
         
      console.log(x)
      /* 
        4
        5
        6
      */
    }
    
  • 生成器を用いた充填配列
  • function* zeros(n) {
         
      while (n--) {
         
        yield 0
      }
    }
    console.log(zeros(8)) // zeros {},     
    console.log(Array.from(zeros(8))) // [0, 0, 0, 0, 0, 0, 0, 0],            
    
  • ジェネレータを使用してフィボナッチの数列を実現する
  • function* fibonacci() {
         
      let arr = [0, 1]
      let [prev, curr] = arr
      while (true) {
         
        ;[prev, curr] = [curr, prev + curr]
        arr.push(curr)
        yield arr
      }
    }
    function Fibonacci(n) {
         
      if (n === 1) {
         
        //  1 
        return 0
      } else if (n === 2 || n === 3) {
         
        //  2、3 
        return 1
      } else {
         
        //  4    
        let num = 0
        const fibo = fibonacci()
        for (let i = 3; i <= n; i++) {
         
          num = fibo.next().value
        }
        return num
      }
    }
    console.log(Fibonacci(8).join()) // 0,1,1,2,3,5,8,13
    
    反復可能なオブジェクトを生成
  • は、星番号*yieldを強化し、反復可能なオブジェクトを反復することができるようにし、yield*は、反復可能なオブジェクトを一連の個別出力の値
  • に順次並べ替える.
    function* generatorFn11() {
         
      yield* [1, 2, 3]
    }
    let g12 = generatorFn11()
    for (const x of generatorFn11()) {
         
      console.log(x)
      /* 
        1
        2
        3
      */
    }
    
    //    
    function* generatorFn11() {
         
      for (const x of [1, 2, 3]) {
         
        yield x
      }
    }
    
  • yield*の値は、*関連のディナリーがdone:true**に戻るときのvalueの属性である.
  • は、一般的なローズマリーの場合、done:trueは、ローズマリーの消耗を表し、この値はundefined
  • である.
    function* generatorFn12() {
           
      console.log('iterValue', yield* [1, 2, 3])
    }
    for (const x of generatorFn12()) {
           
      console.log('value', x)
      /* 
      value 1
      value 2
      value 3
      iterValue undefined
    */
    }
    
  • は、ジェネレータ関数によって生成されたローズマリーに対して、done:trueの値は、リターンの値である(return値がないとundefinedに戻る)
  • .
    function* innerGeneratorFn() {
           
      yield 'foo'
      return 'bar'
    }
    function* outerGeneratorFn() {
           
      console.log('iterValue', yield* innerGeneratorFn())
    }
    for (const x of outerGeneratorFn()) {
           
      console.log('value', x)
      /* 
      value foo
      iterValue bar
    */
    }
    
    yield*を使用して、再帰的アルゴリズムを実現する.
  • yield*で再帰を実現し、このとき、生成器は自身
  • を生成することができる.
    function* nTimes(n) {
         
      if (n > 0) {
         
        yield* nTimes(n - 1) //             
        yield n
      }
    }
    for (const x of nTimes(3)) {
         
      console.log(x)
      /*
        1
        2
        3
      */
    }
    
    生成器をデフォルトのディケンサとして使用します.
  • 生成器オブジェクトは、Iterableインターフェースを実現し、生成器関数とデフォルトのディケンサが起動された後、ディケンサ
  • を生成する.
    class Foo2 {
         
      // Foo     ,       
      constructor() {
         
        this.values = [1, 2, 3]
      }
      *[Symbol.iterator]() {
         
        yield* this.values
      }
    }
    const f = new Foo2() //            
    for (const x of f) {
         
      console.log(x)
      /* 
        1
        2
        3
      */
    }
    
    事前終了ジェネレータ
  • 一つのIteratorインターフェースを実現するオブジェクトは、必ずnext()方法があり、もう一つのオプションのreturn()方法があり、生成器は第3の方法throw()
  • がある.
  • return()およびthrow()は、いずれも、強制生成器が閉状態に入るために使用されてもよい
  • .
    function* generatorFn13() {
         }
    let g13 = generatorFn13() //      
    
    console.log(g13.next) // ƒ next() { [native code] }
    console.log(g13.return) // ƒ return() { [native code] }
    console.log(g13.throw) // ƒ throw() { [native code] }
    
    returnreturn()方法は、様々なサブジェネレータオブジェクトの値(すなわち、return()方法のパラメータを返す.
    function* generatorFn14() {
         
      yield* [1, 2, 3]
    }
    let g14 = generatorFn14()
    
    console.log(g14) // generatorFn14 {}
    console.log(g14.return(5)) // {value: 5, done: true}
    console.log(g14) // generatorFn14 {}
    
  • は、return()方式でクローズ状態のジェネレータオブジェクトに入り、後続の呼び出しnext()は、done:true状態を表示し、後続の提供された任意の戻り値は、もはや記憶されたり、伝播されたりしない.
    console.log(g14.next()) // { value: undefined, done: true },     return()
    console.log(g14.next()) // { value: undefined, done: true }
    console.log(g14.next()) // { value: undefined, done: true }
    
  • for-ofなどの内蔵言語構造は、done:trueの重機オブジェクト内部の戻り値を無視する(undefinedを無視する)
  • である.
    let g15 = generatorFn14()
    for (const x of g15) {
         
      x > 1 && g15.return() // x  1      
      console.log(x)
      /* 
        1
        2
            done:true   value(undefined)
      */
    }
    
    throw
  • throw()方法は、一時停止時に、提供されたエラーをジェネレータオブジェクトに注入する.
  • エラーが処理されていない場合、生成器は
  • を閉じる.
    function* generatorFn15() {
           
      yield* [1, 2, 3]
    }
    let g16 = generatorFn15()
    
    console.log(g16) // generatorFn15 {}
    try {
           
      g16.throw('foo') //     
    } catch (err) {
           
      console.log(err) // 'foo'
    }
    console.log(g16) // generatorFn15 {},      ,     
    
  • エラーが発生器関数内部で処理されれば、生成器は閉じられず、実行を再開することができる.エラー処理は対応するyield
  • をスキップします.
    function* generatorFn16() {
           
      for (const x of [1, 2, 3]) {
           
        //        try/catch     -> (          )         
        //            , throw()                
        try {
           
          yield x //  yield        
        } catch (err) {
           
          console.log(err) // 'foo'
        }
      }
    }
    let g17 = generatorFn16()
    
    console.log(g17.next()) // { value: 1, done: false }
    g17.throw('foo') //     
    console.log(g17.next()) // { value: 3, done: false },     yield
    
    まとめ&質問
  • ジェネレータとは?どの関数がジェネレータを定義できますか?
  • ジェネレータオブジェクトはどうやって取得しますか?ジェネレータnext()メソッドのvalue戻り値をどう指定しますか?ジェネレータ関数はいつから実行されますか?
  • は、「生成器オブジェクトのデフォルトのディケンサは自己参照」をどう理解しますか?
  • yieldキーワードのジェネレータにおける役割は何ですか?リセットキーとの戻り値は何が違いますか?
  • 同じジェネレータ方法で生成された異なるジェネレータオブジェクト間に連絡がありますか?
  • ジェネレータ関数とyieldキーワードを使って、それぞれコードで以下の機能を実現してください.
  • は5回反復して、各反復値とインデックス
  • を取得する.
  • は、3より9未満の整数
  • を取得する.
  • は、1から6の自己増加配列
  • を充填する.
  • フィボナッチ数列第20項の数字の値を求めます.(0から計算します.)
  • yield*の役割は何ですか?普通のローズマリー、ジェネレータ関数によって生成されたローズマリーにおいて、yield*の値はそれぞれ何ですか?
  • はどのようにして生成器をデフォルトのローズマリーとして使うのですか?return()とthrow()の方法で、ジェネレータを早期終了させる方法はそれぞれ何ですか?