TokyoFacebookTwitterHatenaPocket取っつきにくい印象のある関数型プログラミング。5でサポートされたMap()とReduce()を使うと、こんなにすっきりとしたコードが書けるのです。Princeが査読を担当しています。最高の内容に仕上げるために尽力してくれたSitePointの査読担当者の皆さんに感謝します。5から追加された、関数型プログラミングをサポートする優れた機能について忘れがちになります。JavaScriptに組み込まれている配列(Array)オブジェクトのネイティブメソッド、map()とreduce()です。map()とreduce()を使ったことがなければ、これをきっかけに始めてみましょう。5を標準でサポートしています。MapとRedueメソッドを使うと、コードがぐっとすっきりし、読むのも管理も簡単になり、関数をもっと使いこなせるようになります。当然のことですが、コードの読みやすさや管理のしやすさを追求するだけでなく、コードを呼び出すときのパフォーマンスについても考慮しなくてはなりません。現在、ブラウザーはループ処理のようなちょっと厄介な従来の技術を使って、より効率よく表示しようとしています。私はいつも、読みやすく、管理しやすいコードを書くことを念頭に置いています。実際使ってみて問題が発生したときにパフォーマンスを最適化します。早すぎる段階での最適化は厄介なことになると考えています。ようなメソッドの利用は、十分に検討する価値があると考えています。パフォーマンスの問題の壁にぶつからないかぎり、私は気楽にコードを書くことを優先し、本当に必要なときにだけパフォーマンスのマイナーチェンジをするようにしています。Mapメソッドを使うMapメソッドは、配列のすべての要素を操作し、変換された内容と同じ長さの新しい配列を生成する、基本的な関数型プログラミング技術です。具体的に、簡単な例で説明しましょう。単語と文字数の配列です。単語の配列を文字数による配列に変換します(これは高性能のアプリケーションで必要になる複雑で難しい例ではありませんが、単純なケースでの動きを理解しておくと、実際に利用するときに対処しやすくなるでしょう)。配列でのforループの記述は、こんな感じになります。4]ここでは変数をいくつか定義しただけです。単語が書いてあるanimals配列、処理を実行した結果が入る空のlengths配列、配列の各ループ内でこれから処理する各項目を一時的に格納するitem変数を定義しました。forループを最適化するために、一時的な内部count変数とloops変数で、forループを設定しました。それから、animals配列内の単語文字数を数えるまで、項目ごとに繰り返しを実行しています。各単語ごとに文字数の長さを計算し、それをlengths配列にプッシュしました。備考:おそらくitem変数がなくても、animals[count]の文字数を直接、なにも代入せずにLengths配列にプッシュすると、もう少し簡潔に処理できたはずです。すればコードはもう少し短くなりますが、このような単純な例でもコードはもっと読みにくくなっていたでしょう。Array(animaLs.Length))としてlengths(訳注:原文ではLengths)配列を初期設定して、プッシュする代わりにインデックスで項目を挿入することもできました。実践でどうやってコードを使いたいかによります。この方法は技術的に間違っていませんので、どのJavaScriptエンジンでもうまくいくでしょう。一度map()の使い方を知ると、使いにくいと感じるかもしれません。map()を使った方法を見てみましょう。4]この方法でも、動物の種類を入れるanimals配列の変数を定義することから始まっています。そのほかに指定したした変数はlengthsだけで、その値は直接、animals配列の各要素に無名のインライン関数をマッピングした結果に割り当てています。この無名の関数は各動物名に実行され、文字数が返されます。その結果、lengthsは、各単語の文字数通り元のanimals配列と同じ配列になりました。この方法にはいくつか特徴があります。元のやり方と比べてかなり短いコードで済むこと。次に、変数の指定がほとんどいらないこと。変数が少ないほど、グローバル名前空間の複雑化を防ぎ、同じコードを使っている他のパートで、同じ名前が付いた変数が衝突するのを防げます。最初から最後まで、変数の値を変えなくてよいのです。variables)を使ったこの威力に気づくでしょう。始めるのに早すぎることはありません。1つのメリットは、名前がついた関数を分割することと、プロセスの中でより簡潔なコードを生成することで、汎用性が高まることです。無名のインライン関数は厄介にみえてコードの再利用が面倒なので、名前付きのgetLength()関数を定義して、次のようなコンテキストでも使えます。4]すっきりしているでしょう。マッピングをツールキットの一部として作ることで、コードが1つの新しい関数にまとまりました。関手(Functor)とはなにか?興味深いことに、配列オブジェクトにマッピングが追加されたことで、ECMAScript5では基本的な配列タイプが充実した関手(Functor)に変わり、関数型プログラミングがより身近なものになりました。関数型プログラミングの元々の定義によると、関手は以下の3つの基準を満たしています。1組の値がある各要素に実行するMap関数を実装しているMap関数は同じサイズの関手を返すこれはJavaScriptの次のステップで学ぶことになるでしょう。Johanssonが作成した素晴らしい映像を見てください。5の新しい機能で、map()に似た機能ですが、reduce()は別の関手を生成する代わりに、どのタイプの結果にもなりうる1つの結果を生成します。animals配列のすべての単語の文字数の合計を計算するとしましょう。こんな感じで始めますね。//10最初に配列を定義したあと、現在の文字数合計を示すtotal変数を作成し、最初は0にします。forループを実行してanimals配列にある各要素に繰り返し処理をするためのitem変数、ループカウンターのためのcount変数、繰り返し処理を最適化するloops変数も作成します。それからforループを実行してanimals配列にある要素すべてに繰り返し処理をして、各文字数をitem変数に割当てます。最後に、各要素の文字数を合計にします。ここでもこの方法に技術的な間違いはありません。配列で始まり、結果で終わっています。reduce()メソッドを使うと、もっとずっと簡単になります。0);console.log(total);何をしているかというと、新しいtotal変数を定義し、2つのパラメータを使ってanimals配列に還元しています。この2つのパラメータとは無名のインライン関数と現在の文字数合計の初期値0です。還元することで配列の中の各要素に対して関数を実行し、その値を文字数合計に加算し、繰り返して値を渡していきます。ここでインライン関数は2つのパラメータを取得します。これまでの集計と現在配列で処理している単語です。関数はtotalの現在値を処理している単語の文字数に足します。備考:reduce()の2つ目の引数を0に設定することで、total変数に数字が入ることに注目してください。この2つ目の引数がなくてもreduceメソッドは動きますが、結果が期待どおりになるとは限りません(現在の文字数合計を外してもJavaScriptが意図したとおりに動くかは試して確認してください)。reduce()メソッドでインライン関数の定義を統合するので、必要以上に複雑に見えるかもしれません。無名のインライン関数を使う代わりに、名前付きの関数を定義してもう一度やってみましょう。0);console.log(total);このコードの方が少し長いですが、必ずしも長い方が悪いというわけではありません。reduceメソッドを使った処理が少し分かりやすくなっているはずです。reduce()メソッドは2つのパラメータを取得します。配列の各要素に適用するaddLengthという新しい関数と現在の文字数に合計するための初期値0です。addLength関数は、現在の文字数と、処理した文字数をつなげる2つのパラメータを取得します。まとめmap()とreduce()を日常的に使うようになると、コードが分かりやすくなり、汎用性も高まり、管理しやすくなるでしょう。JavaScript関数の技術をより使いこなせるようになります。5に追加されたたった2つの新しいメソッドです。この2つの新機能がもたらしたコードのクオリティと開発者の満足度の向上は、パフォーマンスの一時的な効果をはるかに上回っているでしょう。関数の技術を磨いて、アプリケーションにmap()やreduce()が適しているか判断する前に、実践の中でその効果を確かめてください。MoovwebなどでWebエンジニア、ライター、広報、マーケティング・ディレクターを努めました。カリフォルニア大学バークレー校での情報通信社会科学の研究と、MBAの組織行動学を学びながら、ネットワークに対する人間の本能は、人と人とを結びつけるメディアを成長させるのに欠かせないものであると気づきました。