JavaScript で配列遅延評価ライブラリを実装する方法

JavaScript で配列遅延評価ライブラリを実装する方法

概要

プログラミング言語理論において、遅延評価(英語: Lazy Evaluation)は、遅延計算、遅延評価とも呼ばれ、必要に応じて呼び出すとも呼ばれ、コンピュータプログラミングの概念です。その目的は、コンピュータが行う作業を最小限に抑えることです。これには、「遅延評価」と「最小化された評価」という、関連性はあるものの異なる 2 つの意味があります。パフォーマンスの向上に加えて、遅延評価の最も重要な利点は、無限のデータ型を構築できることです。

関数型言語での遅延評価を見て、遅延評価の理解を深めるために JavaScript で最もシンプルな実装を書いてみたいと思いました。 2 つのメソッドが使用され、どちらも基本的な配列の遅延評価を実装するのに 80 行未満しかかかりません。

達成方法

遅延評価では、評価されるたびに値を返すのではなく、計算パラメータを含む評価関数を返します。計算は、値が使用されるたびに実行されます。

遅延操作が複数ある場合、評価関数のチェーンが形成されます。評価が実行されるたびに、各評価関数は前の評価関数を評価し、値を返します。最後に、計算関数が終了すると、終了値が返されます。

具体的な実装

評価関数の終了を決定する

関数の各評価ではさまざまなデータが返されるため、フローが完了したかどうかを判断するためのマーカーとして一意の値を使用する必要があります。たまたま、Symbol() は、他のどの値とも等しくない値を持つ新しいシンボルを作成できます。

const over = シンボル();

定数isOver = 関数(_over) {
  _over === over を返します。
}

生成関数の範囲

範囲関数は、開始パラメータと終了パラメータを受け入れ、評価関数を返し、評価関数を実行して値を返し、終了時に終了値を返します。

const範囲 = 関数 (から、まで) {
  i = からとします。
  関数を返す(){
    もし(i < to){
      私は++
      console.log('範囲\t', i);
      戻る
    }
    戻る;
  }
}

変換関数マップ

評価関数と処理関数を受け入れ、評価関数フローでデータを取得し、データを処理してフローを返します。

const map = 関数 (フロー、変換) {
  関数を返す(){
    定数データ = フロー();
    console.log('map\t', データ);
    isOver(data) を返します ? data : transform(data);
  }
}

フィルター

評価関数を受け入れ、評価関数フロー内のデータをフィルタリングし、一致するデータを検索して返します。

const filter = 関数 (フロー、条件) {
  関数を返す(){
    while(true) {
      定数データ = フロー();
      if (isOver(データ)) {
        データを返します。
      }
      if(条件(データ)) {
        console.log('filter\t', データ);
        データを返します。
      }
    }
  }
}

割り込み機能停止

評価関数を受け入れ、特定の条件に達したときに中断します。クロージャ関数と停止関数を使用してから、テイク関数を実装できます。

const stop = 関数 (フロー、条件) {
  _stop = false とします。
  関数を返す(){
    if (_stop) は戻ります;
    定数データ = フロー();
    if (isOver(データ)) {
      データを返します。
    }
    _stop = 条件(データ);
    データを返します。
  }
}

const take = function(flow, num) {
  i = 0 とします。
  戻り値 stop(flow, (data) => {
    ++i >= num を返します。
  });
}

コレクション関数結合

返されるものはすべて関数なので、最後に join 関数を使用してすべての値を収集し、配列を返す必要があります。

const join = 関数 (フロー) {
  定数配列 = [];
  while(true) {
    定数データ = フロー();
    if (isOver(データ)) {
      壊す;
    }
    配列.push(データ);
  }
  配列を返します。
}

テスト:

const nums = join(take(filter(map(range(0, 20), n => n * 10), n => n % 3 === 0), 2));
console.log(数値);

出力:

範囲 1

地図1

範囲 2

地図2

範囲 3

地図3

フィルター 30

範囲4

地図4

範囲 5

地図5

範囲 6

地図6

フィルター60

よりエレガントな実装

遅延評価は上記の関数 + クロージャを使用して実装されていますが、まだ十分にエレガントではありません。コードの大部分は反復処理と評価が完了したかどうかの判断に費やされています。実際、es6 で遅延評価を実現するには、ジェネレータを使用するというより良い方法があります。ジェネレータは、反復処理を解決し、フローが完了したかどうかを判断するのに役立ちます。ロジックに集中して、より簡潔で理解しやすく、明確に構造化されたコードを書くことができます。

const範囲 = 関数* (から、まで) {
  for(i = from; i < to; i++) {
    console.log('範囲\t', i);
    利回り i;
  }
}

const map = function* (flow, transform) {
  for(フローのconstデータ) {
    console.log('map\t', データ);
    yield(変換(データ));
  }
}

const filter = function* (フロー, 条件) {
  for(フローのconstデータ) {
    console.log('filter\t', データ);
    if (条件(データ)) {
      収量データ;
    }
  }
}

const stop = function*(flow, condition) {
  for(フローのconstデータ) {
    収量データ;
    if (条件(データ)) {
      壊す;
    }
  }
}

const take = 関数 (フロー、数値) {
  count = 0 とします。
  const _filter = 関数 (データ) {
    カウント++
    count >= 数値を返します。
  }
  stop(flow, _filter) を返します。
}

チェーン呼び出しを追加することで完了します。

クラス _Lazy{
  コンストラクタ() {
    this.iterator = null;
  }

  範囲(...引数) {
    this.iterator = range(...args);
    これを返します。
  }

  マップ(...引数) {
    this.iterator = map(this.iterator, ...args);
    これを返します。
  }

  フィルター(...引数) {
    this.iterator = フィルター(this.iterator, ... 引数);
    これを返します。
  }

  (...引数)を取る{
    this.iterator = take(this.iterator, ...args);
    これを返します。
  }

  [シンボル.イテレータ]() {
    this.iterator を返します。
  }

}

関数遅延(){
  新しい_Lazy() を返します。
}

最後に、もう一度テストします。

const nums = lazy().range(0, 100).map(n => n * 10).filter(n => n % 3 === 0).take(2);

for(let n of nums) {
  console.log('num:\t', n, '\n');
}

出力:

範囲 0

マップ0

フィルター 0

番号: 0

範囲 1

地図1

フィルター 10

範囲 2

地図2

フィルター 20

範囲 3

地図3

フィルター 30

数: 30

はい、完了しました。

要約する

このようにして、最も単純な配列遅延評価ライブラリが完成しました。ここでは、遅延評価を単純に実装するだけです。プロジェクトに組み込むには、多くの詳細を追加する必要があります。コードはわずか 80 行なので、遅延評価の原理を明確に理解し、ジェネレーターの理解を深めることができます。

上記は、JavaScript で配列遅延評価ライブラリを実装する方法の詳細です。JavaScript で配列遅延評価ライブラリを実装する方法の詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • メンテナンス可能なオブジェクト指向 JavaScript コードの作成
  • JavaScript を使用してメンテナンス可能なスライドショー コードを作成する
  • JavaScriptのURLオブジェクトとは何かについて話しましょう
  • JavaScript における正規表現の実際的な応用の詳細な説明
  • Javascript実践におけるコマンドモードの詳しい説明
  • 独自のネイティブ JavaScript ルーターを作成する方法
  • JavaScript でアルゴリズムの複雑さを学ぶ方法
  • いくつかの面接の質問を使ってJavaScriptの実行メカニズムを調べる
  • メンテナンス可能なJSコードの書き方を教えます

<<:  mysqld_multi を使用して単一のマシンに複数のインスタンスをデプロイする方法に関する MySQL チュートリアル

>>:  dockerでlnmp環境を構築する方法

推薦する

CSS3プロパティline-clampはテキスト行の使用を制御します

説明: ブロック要素に表示されるテキストの行数を制限します。 -webkit-line-clamp ...

HTMLおよびJSPページがキャッシュされ、Webサーバーから再取得されるのを防ぎます。

ユーザーがログアウトした後、ブラウザの戻るボタンがクリックされると、Web アプリケーションは保護さ...

MySQL コード実行構造例の分析 [シーケンス、分岐、ループ構造]

この記事では、例を使用して MySQL コード実行構造について説明します。ご参考までに、詳細は以下の...

Web面接でよくある質問:リフローとリペイントの原理と違い

目次ブラウザのレンダリングメカニズムリフローと再塗装リフロー逆流を引き起こす行為:再描画再描画を引き...

CCS におけるマージン: トップ崩壊問題を解決する

HTML 構造は次のとおりです。 CCS 構造は次のとおりです。 ページ効果図は次のとおりです。 こ...

nginxカスタム変数と組み込み定義済み変数の使用

概要Nginx では変数を使用して設定を簡素化し、設定の柔軟性を向上させることができます。すべての変...

Vue echarts は水平棒グラフを実現します

この記事では、水平棒グラフを実現するためのvue echartsの具体的なコードを参考までに共有しま...

Mysql GTID Mha 設定方法

Gtid + Mha + Binlog サーバー構成: 1: テスト環境OS: CentOS 6.5...

ubuntu20.04 LTS システムのデフォルト ソース ソース リスト ファイルの変更

誤って source.list の内容を変更し、一連のエラーが発生した場合は、デフォルトのソース フ...

入力と画像を揃えるためにvertical-alignを使用します

input と img を同じ行に配置すると、img タグが常に input より 1 つ上になり、...

マップタグパラメータの詳細な紹介と使用例

マップ タグはペアで表示する必要があります。 <map> ....</map>...

MySQL のデフォルトのストレージ エンジンを変更する方法

mysql ストレージ エンジン: MySQL サーバーはモジュール スタイルを採用しており、特にス...

JavaScript でプライベート メンバーを作成する

目次1. クロージャを使用する2. ES6クラスを使用する3. ES2020提案を使用する4. We...

DockerにRedisをインストールし、パスワードを設定して接続する方法

Redis は分散キャッシュ サービスです。キャッシュは、大規模システムの開発やパフォーマンスの最適...