関数型プログラミング言語は、ほんの一時期、大学内で使用されたことがありましたが、歴史的に見ても、ツールやライブラリーが豊富にはありませんでした。ところが.NET プラットフォームの Haskell の出現により、関数型プログラミングがよりポピュラーになりました。C++や JavaScript など、一部の伝統的なプログラミング言語では、関数型プログラミングのコンストラクトとフィーチャーを取り入れています。多くの場合、JavaScriptで繰り返しのコードを作成すると、体裁の悪いコーディングになってしまいますが、関数型プログラミングを使えば、それらをすべて回避できます。また、関数型プログラミング・スタイルを使って、よりエレガントなコールバックを書くこともできます。
関数型プログラミングは、とても異なる方法でプログラムを組み立てるので、命令型のパラダイムに慣れているプログラマーにとっては、学習が難しいと思うかもしれません。この記事では、関数型スタイルでJavaScript のすぐれたエレガントなコードを書く例を示し、さらに次の項目について説明します。
- 関数型プログラミングの概念。匿名関数、関数コールの異なる方法、および他の関数に引数として関数を渡す方法を説明します。
- 関数型の概念を使用する。配列ソートの拡張、DHTML (dynamic HTML) 生成のためのエレガントなコード、および関数シーケンスの適用などの例を示します。
多くの開発者は、「どんな方法で」を記述することによって、問題を解決する方法を指定するコーディングの方法を知っています。例えば、階乗を計算する関数をコーディングするには、すべての数の積を求めるために、ループをコーディングするか、または再帰を使ってプログラムを記述します。どちらのケースでも、計算の手順がプログラム内に詳細に記述されます。リスト 1 は、階乗計算のための C コードの例を示しています。
リスト 1. 手続き型での階乗計算
int factorial (int n) { if (n <= 0) return 1; else return n * factorial (n-1); } |
このような言語は、問題を解決するための手続き (手順) を定義するので、手続き型プログラミング言語とも呼ばれています。関数型プログラミングは、その考え方からして明らかに異なります。関数型プログラミングでは、問題が「何か」を記述する必要があり、宣言型とも呼ばれます。階乗を計算する同じプログラムは、nまでのすべての数の積として書くことができます。階乗計算のための典型的な関数型プログラムは、リスト 2 の例のようになります。
リスト 2. 関数型での階乗計算
factorial n, where n <= 0 := 1 factorial n := foldr * 1 take n [1..] |
2 番目のステートメントは、1 から始めて、最初の n 個の数のリストをとり (taken [1..])、1 とそれらの数の積を求めます。この定義には、前のケースのようにループまたは再帰はありません。これは、階乗関数の数学の定義に似ています。ライブラリー関数(take および foldr) の意味と表記 (リスト表記 [ ]) が分かってしまえば、コーディングはとても簡単で、読みやすいものになります。
歴史的には、さまざまな理由で、関数型プログラミング言語はあまりポピュラーなものではありませんでした。しかし、最近、関数型プログラミング言語がコンピューター業界に参入してきました。その1 つの例は、.NET プラットフォームの Haskell です。他のケースでは、既存の言語が、関数型プログラミング言語の概念を借りたものです。iteratorおよび continuation が、C++ を実装した中や JavaScript の機能コンストラクトの中に使われています。ただし、関数型コンストラクトを使うことで、言語全体のプログラミング・パラダイムが変わるわけではありません。JavaScriptは、関数型コンストラクトを追加したからといって、関数型プログラミング言語にはなっていません。
ここで、JavaScript の関数型コンストラクトのさまざまな良い点、日々のコーディングや作業に関数型コンストラクトを使う方法について説明します。まず基本的な機能から始め、関数型コンストラクトを使った興味深いアプリケーションをいくつか見ていきます。
JavaScript では、匿名関数、つまり名前のない関数を書くことができます。それではなぜ匿名関数が必要なのでしょうか?あまり考えずに話を進めてしまいますが、まずは最初に匿名関数の書き方を学びましょう。次のJavaScript 関数があるとします。
リスト 3. 通常の関数
function sum(x,y,z) { return (x+y+z); } |
それに対応する匿名関数は、次のようになります。
リスト 4. 匿名関数
function(x,y,z) { return (x+y+z); } |
この関数を使うには、次のように書きます。
リスト 5. 匿名関数を適用する
var sum = function(x,y,z) { return (x+y+z); }(1,2,3); alert(sum); |
関数を値として使うこともできます。値が割り当てられている関数とともに変数を持つことができます。最後の例は、次のように記述することもできます。
リスト 6. 関数の割り当てを使う
var sum = function(x,y,z) { return (x+y+z); } alert(sum(1,2,3)); |
上記のリスト 6 の例では、変数 sum が関数定義そのものに割り当てられています。 このため、sumは関数であり、どこからでも呼び出せます。
JavaScript では、リスト 7 とリスト 8 に示したように、2 つの方法で関数をコールすることができます。
リスト 7. 標準的な関数の適用
alert (“Hello, World!"); |
または
リスト 8. 式として関数を使用する
(alert) (“Hello, World!"); |
したがって、次のように書くこともできます。
リスト 9. 定義直後での関数の適用
( function(x,y,z) { return (x+y+z) } ) (1, 2, 3); |
括弧の中に関数式を書いてから、引数を渡して評価することもできます。リスト 8 の例では関数名が直接括弧で囲まれていますが、リスト 9 に示すように、関数を使うときに必ず関数名を直接括弧で囲む必要があるわけではありません。
他の関数に、引数として関数を渡すこともできます。 これは新しい概念ではありませんが、以降の例で広く使用されています。リスト 10 に示すように、関数の引数を渡すことができます。
リスト 10. パラメーターとして関数を渡し、それを適用する
var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); }; var sum = function(x,y,z) { return x+y+z; }; alert( passFunAndApply(sum,3,4,5) ); // 12 |
最後の alert ステートメントを実行すると、値 12 を出力します。
前のセクションでは、関数型のスタイルを使ったプログラミングの概念を説明しました。紹介した例は、すべての概念を完全にカバーするものではなく、重要さの順序になっているわけでもありませんが、ここで議論している概念を説明したものです。JavaScriptの関数型スタイルを簡単に要約すると、以下のとおりです。
- 関数には、いつでも名前が必要というわけではない。
- 関数は、他の値と同様に変数に割り当てられる。
- 関数式を書いて、後で使用するために括弧で囲むことができる。
- 関数は他の関数に引数として渡すことができる。
このセクションでは、関数型の概念を効果的に使って JavaScript ですぐれたエレガントなコードを書く方法を、例をあげて示します。(JavaScriptの関数型スタイルを使えば、ここで議論する対象以外のさまざまなことを行えます。)
- 配列ソートを拡張する
-
配列の要素を日付に従ってソートできる、sort メソッドを書いてみましょう。JavaScriptでこれを書くのはとても簡単です。配列オブジェクトの sort メソッドは、オプションのパラメーターとして比較関数を使います。ここでは、リスト 11 に示す比較関数が必要です。
リスト 11. 比較関数function (x,y) { return x.date - y.date;}
必要な関数を入手するために、リスト 12 の例を使います。
リスト 12. sort 関数の拡張arr.sort( function (x,y) { return x.date ? y.date; } );
arr は配列型のオブジェクトです。ここでは、arr 内のすべてのオブジェクトを、それぞれの日付に従ってソートします。ソート操作を完了させるために、比較関数が、その定義とともにsort 関数に渡されます。この関数を使うと、以下のようになります。
- 各 JavaScript オブジェクトは date プロパティーを持ちます。
- JavaScript の配列型の sort 関数は、ソートに使う比較関数をオプションのパラメーターとしてとります。これは、Cライブラリーの qsort 関数に似ています。
- DHTML 生成のためのエレガント・コード
- この例では、配列から DHTML を生成するための、エレガントなコードの書き方を示しています。配列から取得した値から、テーブルを生成することができます。または、配列の内容を使って、順番通りに並べられたリストまたはランダムな順番のリストを生成できます。また、縦方向または横方向のメニュー項目を生成することもできます。
リスト 13 のコーディング・スタイルは、配列から DHTML を生成するために通常使われます。
リスト 13. DHTML を生成するための通常のコードvar str=' '; for (var i=0;i<arr.length;i++) { var element=arr[i]; str+=... HTML generation code... } document.write(str);
このコードを、リスト 14 のコードを使用して置き換えることができます。
リスト 14. DHTML を生成する一般的な方法Array.prototype.fold=function(templateFn) { var len=this.length; var str=' '; for (var i=0 ; i<len ; i++) str+=templateFn(this[i]); return str; } function templateInstance(element) { return ... HTML generation code ... } document.write(arr.fold(templateInstance));
Array 型の prototype プロパティーを新しい関数 fold を定義しました。この関数は後に定義するどの配列でも使うことができます。
- 関数シーケンスの適用
- コールバック関数として関数セットを使いたい場合を考えてみましょう。この目的のために、2つのパラメーターを持つ window.setTimeout 関数を使います。最初のパラメーターは、2番目のパラメーターが示す時間 (ミリ秒) が経過した後に呼び出される関数です。リスト 15 は、これを行う 1 つの方法を示しています。
リスト 15. コールバックで関数セットをコールするwindow.setTimeout(function(){alert(‘First!’);alert(‘Second!’);}, 5000);
リスト 16 は、もっとエレガントにこれを行う方法を示しています。
リスト 16. 関数シーケンスをコールするエレガントな方法Function.prototype.sequence=function(g) { var f=this; return function() { f();g(); } }; function alertFrst() { alert(‘First!’); } function alertSec() { alert(‘Second!’); } setTimeout( alertFrst.sequence(alertSec), 5000);
あるコールバックをコールした後、イベントを処理している間に、別のコールバックをコールしたい場合は、リスト 16 のコードを拡張することもできます。おそらくこれは、皆さんが追求するととても興味深い勉強になるでしょう。
日々エレガントに活動するために、JavaScript における関数型プログラミングの概念を多くの分野に適用できます。この記事で紹介した例は、ほんの一部のケースにすぎません。関数型プログラミングの正しいシナリオを確認して、その概念を適用すれば、多くのことを
理解できるようになり、よりエレガントになれるでしょう。
学ぶために
-
「Functional programming in the Java language: Use closures and higherorder functions to write modular Java code」 (developerworks, 2004 年 7 月): 適切に構造化されたモジュラー・コードをJava 言語で書くための、クロージャーおよび高階関数などの関数型プログラミング・コンストラクトを使う方法を学びます。
-
「Beginning Haskell」 (developerWorks, 2001 年 9 月): このチュートリアルでは、関数型プログラミングのパラダイムを、Haskell98 言語における具体例をあげて紹介します。
-
Tutorial Papers in Functional Programming: 関数型プログラミングに関するトピックのリソースを見つけてください。
- developerWorks Web Architecture ゾーン: Web テクノロジーに特化された記事およびチュートリアルで、サイト開発のスキルを高めてください。
-
developerWorks technical events and webcasts: 短期間で習得できるたくさんの情報が詰まったテクニカル・セッションで、最新の情報を入手し、難しいソフトウエア・プロジェクトの品質および結果の向上に役立ててください。
製品や技術を入手するために
-
無料ダウンロードとリソースの学習: developerWorks からソフトウェアをダウンロードして、作業の向上を図ってください。
議論するために
-
developerWorks ディスカッション・フォーラムに参加してください。
-
developerWorks ブログから developerWorks のコミュニティーに参加してください。