SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

F#で学ぶ関数型プログラミング入門

関数型プログラミングの主な概念とF#での利用例

関数型の基本:型、値、バインディング

 ごく簡単に説明すると、関数型プログラミングは、値の抽象性を高め、値を操作する方法を改善します。そのために、通常の標準データ型やクラスを超える、次のようなデータ抽象化メカニズムが用意されています。

  • 弁別子付き共用体(閉じたデータ表現を作成できる)
  • タプルとレコード(データの集合をより簡単に作成できる)
  • 関数型(シグネチャの定義と強制を可能にする)
  • ポリモーフィックな抽象データ型(ジェネリックな構造化データを表現できる)
  • シーケンス(オンデマンドで操作できるコレクション - 例えば、乱数の無限ストリームを作成し、好きなだけ値を読み取ることができる)

 これらの型は、次のようにわずかなコードで定義できます。

// 名前と年齢を持つ個人。レコードとして定義する。
type Person = { name: string; age: int; }

// 名前とその妥当性を表すペア/タプル。
type NameValid = string * bool

// ジェネリックな述語型(値をブール値にマップする関数)
type 'a predicate = 'a -> bool

// 以下の表現は等価
type IntList = int list
type IntList' = List

// すべての自然偶数は(ほぼ)無限のシーケンスとして表現される。
let evenNumbers = seq { 0 .. 2 .. Int32.max_int }

 最後の行は、値の定義、つまり値と名前のバインディングです。ここではトップレベルのバインディング(モジュールレベルのバインディング)で表現していますが、letバインディングはプログラムのどこでも使用できます。任意のレベルにネストされたローカル関数と定義を簡単に作成できます。

 関数型プログラミングでは、関数はファーストクラスの値としてデータ値とまったく同じ扱いとなるため、関数をデータ構造に埋め込んで、他の関数に渡したり他の関数から受け取ることができます。このような関数を高階関数と呼びます。この機能によって、クラス型は複数の関数フィールドを持つレコードとなるため、関数型プログラミング言語はオブジェクト指向機能を容易に組み込むことができます。

不変性、クロージャ、型推論とは

 関数型プログラミングの根本概念は「不変性」です。つまり、作成した値が変わらないことです。オブジェクト指向言語や命令型言語では変数を使って値を変更するのが当たり前ですから、それとは好対照です。関数型プログラミングでは、値を変更するのではなく、変更された新しいコピーを作成します。具体的には、通常の、パラメータが状況に応じて変化する命令型void関数を、パラメータのコピーを変更して返す関数に置き換えます。新しいコピーを作成するのは不経済と感じるかもしれませんが、そうではありません。コンパイラによって巧妙な戦略が適用され、コピーおよび変更の一連の操作は大幅に最適化されます。条件によっては、命令型コンパイラをも超える性能を発揮できます。

 そうなると、変数を使う必要はあるのでしょうか。厳密に言えば、使う必要はありません(命令型コードを書こうとする場合は話が別ですが)。次のようなコードがあるとしましょう。

let printSumDoubled x y = printf “The result is = %d\n” ((x+y)*2)

let _ =
    let a, b = 172, 201
    printSumDoubled a b
    let a, b = 215, 347
    printSumDoubled a b

 1行目は、2つの引数の合計を2倍して、その結果をプリントする関数の定義です。データ型を記述しなくてよいことに注目してください。コンパイラがパラメータの使い方を見て、型を判断します。この例では、xをyに足すのですから、xとyはどちらも必然的に整数型です(他の型を宣言しない限り、これが算術演算子のデフォルトの型です)。また、正規パラメータをかっこで囲む必要もなければ、パラメータをカンマで区切る必要もないことも注目に値します。かっこやカンマを使うこともできますが、それは関数が2つの引数ではなく1つのタプル引数を受け取るという意味になるため、関数呼び出しをprintSumDoubled(a, b)のように変える必要があります。心配しなくても、関数に渡す引数が多すぎる場合は、コンパイラがそれを指摘してくれます。しかし、クロージャ(自らの状態情報を内部に持つ関数)を作成するときには、次のように渡す引数が少ない方が良いでしょう。

let printSumWithFiveDoubled = printSumDoubled 5

 このprintSumWithFiveDoubledは、1つの整数型引数を受け取り、その値に5を足してから2倍した結果をプリントする関数です。基本的にはprintSumDoubled関数をインスタンス化したものですが、その事実は次のように定義した方が理解しやすくなるでしょう。

let printSumWithFiveDoubled y = printSumDoubled 5 y

 上記のコードブロックのletバインディングは、1行ごとに1つの代入を記述してこれらを連結しています。しかし、水面下ではずっと複雑なことが行われます。ブロックの1行目で、値(172, 201)は左側の(a, b)のパターンにマッチします。単純明快なマッチングなので、これらの値はそれぞれaとbにバインディングされます。当然ながら、任意の複雑な式に対してパターンマッチを指定し、多数のバインディングを一度に作成することもできます。または、まったくバインディングを作成しないことも可能です。例えば、_に対するトップレベルのバインディングは、計算式の値をそのまま提出することを意味します。戻り値がないらしい関数(C#のvoidに相当するもの)は、戻り値の型がunitになります。実際には、この型は()として表現される1つの"値なし"を値として持ちます。上記の例では、この空の値は無視されます。

 ここで重要なのは、バインディングは通常の意味での変数とは異なるということです。バインディングは特定の値を示す名前でしかなく、別の値を代入すると前の値はその陰に隠れてしまいます。実際、上記のコードは次のように記述しても同じです。

let printSumDoubled x y = printf “The result is = %d\n” ((x+y)*2)

let _ =
    let a, b = 172, 201
    printSumDoubled a b
    let newA, newB = 215, 347
    printSumDoubled newA newB

次のページ
型とパターンマッチングを改善する

この記事は参考になりましたか?

japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Adam Granicz(Adam Granicz)

F#の開発、トレーニング、コンサルティングのトップ企業IntelliFactory社のCEO。F#に関する最も包括的なガイドである『Expert F#』をF#言語デザイナのDon Symeと共同で執筆。関数型プログラミング言語を研究し、カリフォルニア工科大学(California Institute of Technology)の修士課程を修める。メールでの連絡先はgranicz.adam@intellifactory.com

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/3041 2008/10/10 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー