高度なJavaScriptガイド:ジェネレーター。パート2、簡単な使用例



前の記事で説明したジェネレーターの動作は 複雑ではありませんが、それは間違いなく驚くべきことであり、最初は混乱しているように見えるかもしれません。したがって、新しい概念を学習する代わりに、一時停止して、ジェネレーターの使用の興味深い例を見ていきます。



次のような関数を作成しましょう。



function maybeAddNumbers() {
    const a = maybeGetNumberA();
    const b = maybeGetNumberB();

    return a + b;
}

      
      





関数 maybeGetNumberA



maybeGetNumberB



戻り値ですが、null



またはを返す 場合もあり undefined



ます。これは、彼らの名前に「たぶん」という言葉が含まれていることからも明らかです。これが発生した場合は、これらの値(たとえば、数値とnull



)を入力しようとしないでください。たとえば、 停止して戻ることをお勧めし null



ます。つまり null



null



/ undefined



に数値または他の null



/を 追加することによって得られる予測できない値ではありません undefined







したがって、数値が実際に定義されていることを確認する必要があります。



function maybeAddNumbers() {
    const a = maybeGetNumberA();
    const b = maybeGetNumberB();

    if (a === null || a === undefined || b === null || b === undefined) {
        return null;
    }

    return a + b;
}

      
      





すべてが動作しますが、それは場合 a



である null



undefined



、そして、関数を呼び出しても意味がありません maybeGetNumberB



とにかく返されることはわかっています null







関数を書き直してみましょう:



function maybeAddNumbers() {
    const a = maybeGetNumberA();

    if (a === null || a === undefined) {
        return null;
    }

    const b = maybeGetNumberB();

    if (b === null || b === undefined) {
        return null;
    }

    return a + b;
}

      
      





そう。単純な3行のコードの代わりに、すぐに10行に膨らませました(空のコードは数えません)。そして、関数が適用されました if



。関数が何をするのかを理解するために、それを通り抜ける必要があります。そして、これは単なる教育的な例です!はるかに複雑なロジックを備えた実際のコードベースを想像してみてください。このようなチェックはさらに困難になります。ここでジェネレーターを使用して、コードを簡略化したいと思います。



見てください:



function* maybeAddNumbers() {
    const a = yield maybeGetNumberA();
    const b = yield maybeGetNumberB();

    return a + b;
}

      
      





私たちは、発現させことができれば yield <smething>



、それがある場合は、テストを <smething>



実際の値ではなく、 null



undefined



?数値ではないことが判明した場合null



は、前のバージョンのコードと同様に、停止して戻るだけ です。



つまり、実際の定義された値でのみ機能するよう見えるコードを記述でき ます。ジェネレーターはこれをチェックし、適切なアクションを実行できます。魔法でしょ?そしてそれは可能であるだけでなく、書くのも簡単です!



もちろん、ジェネレータ自体にはこの機能はありません。それらはイテレータを返すだけであり、必要に応じて値をジェネレータに挿入して戻すことができます。したがって、ラッパーを作成する必要があり runMaybe



ます。



関数を直接呼び出す代わりに:



const result = maybeAddNumbers();

      
      





これをラッパー引数と呼びます。



const result = runMaybe(maybeAddNumbers());

      
      





このパターンは、ジェネレーターで非常に一般的です。彼ら自身はあまり知りませんが、自作のラッパーの助けを借りて、ジェネレーターに望ましい動作を与えることができます!これが私たちが今必要としているものです。



runMaybe



-1つの引数を取る関数:ジェネレーターによって作成されたイテレーター:



function runMaybe(iterator) {

}

      
      





このイテレータをループで実行してみましょう while



これを行うには、イテレータを初めて呼び出して、そのプロパティのチェックを開始する必要があります done







function runMaybe(iterator) {
    let result = iterator.next();

    while(!result.done) {

    }
}

      
      





ループ内には、2つの可能性があります。result.value



null



またはの 場合 undefined



、反復をすぐに停止して、を返す必要があります null



これをやろう:



function runMaybe(iterator) {
    let result = iterator.next();

    while(!result.done) {
        if (result.value === null || result.value === undefined) {
            return null;
        }
    }
}

      
      





ここでは、ヘルプreturn



を使用して反復すぐに停止し、ラッパーから戻ります null



しかし、それresult.value



が数値の場合 は、ジェネレーターに「戻る」必要があります。たとえば、 yield maybeGetNumberA()



関数 maybeGetNumberA()



yield maybeGetNumberA()



数値の場合、その数値の値を置き換える必要があります 私に説明してみましょう:のは、計算の結果は言わせ maybeGetNumberA()



、その後、我々は置き換え、5である const a = yield maybeGetNumberA();



const a = 5;



ご覧のとおり、抽出された値を変更する必要はありません。ジェネレータ戻すだけで十分 です。 メソッドに引数として渡すことで、ある値に



置き換えることができることを覚えています。 yield <smething>



next



イテレータ内:



function runMaybe(iterator) {
    let result = iterator.next();

    while(!result.done) {
        if (result.value === null || result.value === undefined) {
            return null;
        }

        // we are passing result.value back
        // to the generator
        result = iterator.next(result.value)
    }
}

      
      





ご覧のとおり、新しい結果は再び変数に格納されます result



。これは、をresult



使用して明確に宣言した ために可能 let



です。



ここで、値を取得するときにジェネレーターがnull



/ に遭遇した場合 、ラッパーから undefined



戻るだけ です。 / を検出せずに反復プロセスが終了するように、他に何かを追加する必要があります 。結局のところ、2つの数値を取得した場合、ラッパーからそれらの合計を返す必要があります。 ジェネレータ は式で終了します 。私たちはその存在を理解しています null



runMaybe







null



undefined







maybeAddNumbers



return



return <smething>



ジェネレーターで、呼び出しからnext



オブジェクト を返します { value: <smething>, done: true }



これが発生while



すると、プロパティdone



が値を取得するため、ループ が停止 します true



ただし、最後に返された値(この特定のケースではこれ a + b



)は引き続きプロパティに格納されます result.value



そして、私たちはそれを返すことができます:



function runMaybe(iterator) {
    let result = iterator.next();

    while(!result.done) {
        if (result.value === null || result.value === undefined) {
            return null;
        }

        result = iterator.next(result.value)
    }

    // just return the last value
    // after the iterator is done
    return result.value;
}

      
      





そしてそれがすべてです!



関数maybeGetNumberA



とを作成 maybeGetNumberB



、最初に実数を返すようにします。



const maybeGetNumberA = () => 5;
const maybeGetNumberB = () => 10;

      
      





コードを実行して結果をログに記録しましょう。



function* maybeAddNumbers() {
    const a = yield maybeGetNumberA();
    const b = yield maybeGetNumberB();

    return a + b;
}

const result = runMaybe(maybeAddNumbers());

console.log(result);

      
      





予想通り、数15は、コンソールに表示されます。



さて、との条件のいずれかを交換してください null







const maybeGetNumberA = () => null;
const maybeGetNumberB = () => 10;

      
      





コードを実行すると、次のようになります null







ただし、/を 返す maybeGetNumberB



場合は、関数が呼び出されない ようにすることが重要です 。計算が成功したことをもう一度確認しましょう。これを行うには、2番目の関数に追加するだけです maybeGetNumberA



null



undefined



console.log







const maybeGetNumberA = () => null;
const maybeGetNumberB = () => {
    console.log('B');
    return 10;
}

      
      





ラッパーを正しく記述している runMaybe



場合、このコードを実行すると、文字 はコンソールに表示されB



ません



実際、コードを実行すると、単純にが表示され null



ます。これは、ラッパーがnull



/を 検出するとすぐにジェネレーターを実際に停止することを意味します undefined







コードは意図したとおりに機能します。null



任意の組み合わせを生成し ます。



const maybeGetNumberA = () => undefined;
const maybeGetNumberB = () => 10;
const maybeGetNumberA = () => 5;
const maybeGetNumberB = () => null;
const maybeGetNumberA = () => undefined;
const maybeGetNumberB = () => null;

      
      





等。



しかし、この例の利点は、この特定のコードの実行にありません。これは、/ を抽出できる任意のジェネレーターで 動作できるユニバーサルラッパーを 作成したという事実にあります より複雑な関数を書いてみましょう。 null



undefined











function* maybeAddFiveNumbers() {
    const a = yield maybeGetNumberA();
    const b = yield maybeGetNumberB();
    const c = yield maybeGetNumberC();
    const d = yield maybeGetNumberD();
    const e = yield maybeGetNumberE();
    
    return a + b + c + d + e;
}

      
      





あなたは問題なく私たちのラッパーでそれを行うことができます runMaybe



!実際、関数が数値を返すことはラッパーにとっても重要ではありません。結局のところ、数値型については触れていません。したがって、ジェネレーターで任意の値(数値、文字列、オブジェクト、配列、より複雑なデータ構造)を使用でき、ラッパーで機能します。



これが開発者を刺激するものです。ジェネレーターを使用すると、コードにカスタム機能を追加できます。これは非常に一般的に見えます(もちろん、呼び出しは別として yield



)。ジェネレーターを特別な方法で反復するラッパーを作成する必要があります。したがって、ラッパーはジェネレーターに必要な機能を追加します。これは何でもかまいません。発電機にはほぼ無限の可能性があります。それはすべて私たちの想像力に関するものです。



All Articles