JavaScriptインタビューをマスターする:関数構成とは何ですか?

Google Datacenter Pipes — Jorge Jorquera —(CC-BY-NC-ND-2.0)
「JavaScriptのインタビューをマスターする」は、中級から上級レベルのJavaScriptポジションに応募する際に遭遇する可能性のある一般的な質問の候補者を準備するための一連の投稿です。これらは、実際のインタビューでよく使用する質問です。

関数型プログラミングはJavaScriptの世界を引き継いでいます。ほんの数年前、関数型プログラミングとは何かを知っているJavaScriptプログラマーはほとんどいませんでしたが、過去3年で見た大規模なアプリケーションコードベースはすべて、関数型プログラミングのアイデアを多用しています。

関数合成は、2つ以上の関数を組み合わせて新しい関数を生成するプロセスです。関数を一緒に構成することは、データが通過する一連のパイプを一緒にスナップするようなものです。

簡単に言えば、関数「f」と「g」の組み合わせは、「f(g(x))」として定義できます。これは、内側から外側へ、つまり右から左へ評価します。つまり、評価順序は次のとおりです。

  1. 「x」
  2. 「g」
  3. 「f」

これをコードでもっと詳しく見てみましょう。ユーザーのフルネームをURLスラッグに変換して、各ユーザーにプロファイルページを提供するとします。そのためには、一連の手順を実行する必要があります。

  1. 名前をスペースの配列に分割する
  2. 名前を小文字にマッピングします
  3. ダッシュで参加する
  4. URIコンポーネントをエンコードする

簡単な実装を次に示します。

悪くありません…しかし、もっと読みやすいと言ったらどうでしょうか?

これらの各操作には、対応する構成可能な機能があると想像してください。これは次のように書くことができます。

これは最初の試みよりも読みにくいように見えますが、そこに固執し、どこかに行きます。

これを達成するために、「split()」、「join()」、「map()」などの一般的なユーティリティの構成可能な形式を使用しています。実装は次のとおりです。

`toLowerCase()`を除き、これらすべての関数の製品テスト済みバージョンはLodash / fpから入手できます。次のようにインポートできます。

「lodash / fp」から{カレー、マップ、結合、分割}をインポートします。

またはこのように:

const curry = require( 'lodash / fp / curry');
const map = require( 'lodash / fp / map');
// ...

私はここで少し怠け者です。このカレーは技術的には実際のカレーではなく、常に単項関数を生成することに注意してください。代わりに、単純な部分的なアプリケーションです。 「カレーと部分アプリケーションの違いは何ですか?」を参照してください。ただし、このデモンストレーションの目的では、実際のカレー機能と互換性があります。

`toSlug()`実装に戻ると、本当に気になっていることがあります。

それは私には多くのネストのように見えます、そしてそれは読むのが少し混乱しています。これらの関数を自動的に構成する関数でネスティングをフラット化できます。つまり、ある関数からの出力を取得し、最終値を吐き出すまで次の関数の入力に自動的にパッチを適用します。

考えてみると、そのようなことをするように聞こえる配列エクストラユーティリティがあります。値のリストを受け取り、それらの値のそれぞれに関数を適用して、単一の結果を蓄積します。値自体を関数にすることができます。この関数は「reduce()」と呼ばれますが、上記の構成動作に一致させるには、左から右ではなく、右から左に減らす必要があります。

私たちが探しているものとまったく同じことをする `reduceRight()`があるのは良いことです。

`.reduce()`と同様に、配列 `.reduceRight()`メソッドは、reducer関数と初期値( `x`)を取ります。配列関数を(右から左に)繰り返し、それぞれを累積値( `v`)に適用します。

コンポーズを使用すると、ネストせずに上記のコンポジションを書き換えることができます。

もちろん、 `compose()`にはlodash / fpも付属しています:

import {compose} from 'lodash / fp';

または:

const compose = require( 'lodash / fp / compose');

構図の数学的な形式、裏返しで考えている場合、構成は素晴らしいですが、左から右の順序で考えたい場合はどうでしょうか。

一般に「pipe()」と呼ばれる別のフォームがあります。 Lodashはそれを `flow()`と呼びます:

実装は「compose()」とまったく同じであることに注意してください。ただし、「。reduceRight()」の代わりに「.reduce()」を使用します。これにより、右から左ではなく左から右に縮小されます。

`pipe()`で実装された `toSlug()`関数を見てみましょう:

私にとって、これはずっと読みやすいです。

筋金入りの機能プログラマーは、機能構成の観点からアプリケーション全体を定義します。一時変数の必要性を排除するために頻繁に使用します。 `toSlug()`の `pipe()`バージョンを注意深く見ると、何か特別なことに気付くかもしれません。

命令型プログラミングでは、変数に対して変換を実行すると、変換の各ステップで変数への参照が見つかります。上記の `pipe()`実装は、ポイントのないスタイルで記述されています。つまり、動作する引数をまったく識別しません。

ユニットテストやRedux状態レデューサーなどでパイプを頻繁に使用して、ある操作と次の操作の間で一時的な値を保持するためだけに存在する中間変数の必要性を排除しています。

最初は奇妙に聞こえるかもしれませんが、練習を積むと、関数型プログラミングでは、物の名前があまり関係ない、非常に抽象的な一般化された関数で作業していることに気付くでしょう。名前が邪魔になります。変数を不必要な定型句と考え始めるかもしれません。

そうは言っても、ポイントフリーのスタイルは行き過ぎだと思う。密度が高くなりすぎて理解しづらくなる可能性がありますが、混乱した場合は、ここにちょっとしたヒントがあります。

使用方法は次のとおりです。

`trace()`は、より一般的な `tap()`の特別な形式であり、パイプを流れる各値に対して何らかのアクションを実行できます。それを得る?パイプ?タップしますか?次のように `tap()`を書くことができます:

これで、 `trace()`が特別なケースの `tap()`であることがわかります。

関数型プログラミングとは何か、部分的なアプリケーションとカリー化が関数合成とどのように連携して、より少ない定型文でより読みやすいプログラムを書くのかを理解し始める必要があります。

シリーズを見る

  • クロージャーとは何ですか?
  • クラス継承とプロトタイプ継承の違いは何ですか?
  • 純粋な機能とは何ですか?
  • 関数構成とは何ですか?
  • 関数型プログラミングとは何ですか?
  • 約束とは何ですか?
  • ソフトスキル

スキルをレベルアップする

Eric ElliottでJavaScriptを学んでください。あなたがメンバーでない場合、あなたは逃しています!

エリック・エリオットは、「JavaScriptアプリケーションのプログラミング」(O’Reilly)の著者であり、高度なJavaScriptと開発リーダーシップのカリキュラムです。彼は、Adobe Systems、Zumba Fitness、The Wall Street Journal、ESPN、BBC、およびUsher、Frank Ocean、Metallicaなどのトップレコーディングアーティストのソフトウェアエクスペリエンスに貢献しています。

彼は、世界で最も美しい女性と、どこでも好きな場所で仕事をしています。