エレガントな JavaScript を作成するための関数型プログラミングの使用

関数型プログラミング、つまり宣言型プログラミングは、非常に強力なプログラミング・メソッドであり、ソフトウェア業界で評判を得ています。この記事では、関数型プログラミングに関連する概念を紹介し、その概念を有効に使った例を示します。また関数型プログラミングのコンストラクトとフィーチャーをJavaScript™ に取り入れ、JavaScript でエレガントなコードを作成する方法について説明します。

Shantanu Bhattacharya, Chief Consultant, Siemens Information Systems Limited

Photo of Shantanu BhattacharyaShantanu Bhattacharya は、アプリケーション・ソフトウェア、ネットワーク・ソフトウェア、セキュリティー・ソフトウェアの設計、作成で活躍しています。最も注目に値する業績は、インド初のスーパーコンピューター用ファイル・システム、および Indian Missile Program 用のリアルタイム・ソフトウェアです。彼の名前での出版物は複数あり、ACM Computing Surveys の調査員でもあります。現在はインドのバンガロールにある Siemens Information Systems Limited でチーフ設計者として働いています。



2006年 6月 13日

はじめに

関数型プログラミング言語は、ほんの一時期、大学内で使用されたことがありましたが、歴史的に見ても、ツールやライブラリーが豊富にはありませんでした。ところが.NET プラットフォームの Haskell の出現により、関数型プログラミングがよりポピュラーになりました。C++や JavaScript など、一部の伝統的なプログラミング言語では、関数型プログラミングのコンストラクトとフィーチャーを取り入れています。多くの場合、JavaScriptで繰り返しのコードを作成すると、体裁の悪いコーディングになってしまいますが、関数型プログラミングを使えば、それらをすべて回避できます。また、関数型プログラミング・スタイルを使って、よりエレガントなコールバックを書くこともできます。

関数型プログラミング

関数型プログラミングは、中間結果を保管する一時変数を使用せずに、プログラムへの入力に対して実行される操作のみを記述します。重要なのは「どんな方法で」ではなく、「何をどんな理由で」を形にすることです。シーケンシャルなコマンドの実行に重点が置かれる手続き型プログラミングとは対照的に、関数型プログラミングは、ステート・マシンの実装よりも、関数定義を重要視します。

大規模なナレッジ管理のアプリケーションでは、関数型プログラミング・スタイルを使うと、開発が単純化できるため、大きな利点になります。

関数型プログラミングは、とても異なる方法でプログラムを組み立てるので、命令型のパラダイムに慣れているプログラマーにとっては、学習が難しいと思うかもしれません。この記事では、関数型スタイルでJavaScript のすぐれたエレガントなコードを書く例を示し、さらに次の項目について説明します。


関数型プログラミングの概念

多くの開発者は、「どんな方法で」を記述することによって、問題を解決する方法を指定するコーディングの方法を知っています。例えば、階乗を計算する関数をコーディングするには、すべての数の積を求めるために、ループをコーディングするか、または再帰を使ってプログラムを記述します。どちらのケースでも、計算の手順がプログラム内に詳細に記述されます。リスト 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) の意味と表記 (リスト表記 [ ]) が分かってしまえば、コーディングはとても簡単で、読みやすいものになります。

ちょうど 3 行の Mirandaコードのパラメーター (そのエレメントは、いずれかの一般的な型をとる)に応じて、幅優先または深さ優先のトラバースを使って n 項のツリーの各エレメントを処理するルーチンを書くことができます。

歴史的には、さまざまな理由で、関数型プログラミング言語はあまりポピュラーなものではありませんでした。しかし、最近、関数型プログラミング言語がコンピューター業界に参入してきました。その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 における関数型プログラミングの概念を多くの分野に適用できます。この記事で紹介した例は、ほんの一部のケースにすぎません。関数型プログラミングの正しいシナリオを確認して、その概念を適用すれば、多くのことを

理解できるようになり、よりエレガントになれるでしょう。

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, Java technology
ArticleID=237352
ArticleTitle=エレガントな JavaScript を作成するための関数型プログラミングの使用
publish-date=06132006