Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

関数型言語としてのF#

C#プログラマのためのF#入門(2)

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2009/10/23 14:00

 本連載では、C#で「最低限」なんらかのコードを書いたことのあるプログラマーを対象に、関数型プログラミング言語「F#」について数回にわたり解説していきます。第2回目は「関数型言語としてのF#」をテーマに、関数型言語特有の機能について紹介していきたいと思います。

目次

はじめに

 C#プログラマのためのF#入門、第2回目は「関数型言語としてのF#」をテーマに、関数型言語特有の機能について紹介していきたいと思います。C#においてもC# 2.0以降、関数型プログラミングの概念が、匿名メソッド、ラムダ式、LINQなどで徐々に取り入れられていますので、まったく目新しいものばかりではないかもしれませんが、関数型言語の主役とも言える「関数」について、C#との比較も交えながら紹介したいと思います。

プロジェクトの作成

 前回はfsi.exeを使用して、簡単な束縛や定義について解説しましたが、今回は、VS2010でF#コンパイラも使用してみましょう。まず、VS2010から[ファイル]-[新しいプロジェクト]-[Visual F#]-[F# Application]でプロジェクトを作成してみましょう。

#light

 F#のプロジェクトを作成する際に最初に考慮しなくてはならないのは、#lightシンタックスのON/OFFです。#lightシンタックスとは、ホワイトスペース(空白)によるインデントを使用した特定の書式を用いることによって、”in”、”with”、 “begin”、”end”、”;;”などの省略が可能になり、読みやすいコーディングを可能にする簡易構文モードのことです。半角4スペースがインデントの単位になります。8月15日時点で、VS2010のF#のソリューションファイルのデフォルトは簡易構文モード(#light)になっています。OFFにするには、コードの先頭に#light "off" と記述します。使用方法については以下の例を参考にしてください。

(1)複数の式を改行して記述する

[リスト1]#lightシンタックスがON 複数の式の記述
#light
printf "Hello, world"      //改行のみでOk
printf "Good, morning"
[リスト2]#lightシンタックスがOFF 複数の式の記述
#light "off"
printf "Hello, world" ;;   //ダブルセミコロンにて区切る必要がある
printf "Good, morning"

(2)束縛の有効範囲とインデントの使用

[リスト3]#lightシンタックスがON インデントの使用
#light
let f x =
    let y = 1    //インデントをそろえる必要がある
    let z = 2    //インデントをそろえる必要がある
    x * y + z    //インデントをそろえる必要がある
[リスト4]#lightシンタックスがOFF インデントの使用
#light "off"
let f x =
          let y = 1 in    //インデントがそろっていなくても実行可能。inが必要
        let z = 2 in      //インデントがそろっていなくても実行可能。inが必要
        x * y + z

再帰

 ここからは、いよいよ関数の説明です。最初にF#の最も特徴的とも言える関数、「再帰関数」について解説したいと思います。

再帰関数

 再帰関数とは、自分自身を呼び出す関数のことです。再帰はC#でも利用されているアイデアですが、例としてnの階乗を求める再帰関数を解説します。

 上記で作成したプロジェクトのソースファイルに、下記のようにタイプしてください。

[リスト5]再帰関数factoの定義とその呼び出し
#light  //デフォルトで#lightモードですが、分かりやすいのであえて記述

let rec facto n =  if n <= 1 then 1 else n * facto (n-1)

printfn "%d" (facto 5)

 再帰関数factoの定義の1文をコピーして、VS2010のボトムにある、「F# Interactive」に貼り付けてください。F# Interactive とはF#インタープリタのことで、VS2010では「F# Interactive」というラベルが付いているツールウィンドウで使用可能です。

 このツールウィンドウが開いていない場合には、[Ctrl]+[Alt]+[F]、もしくは、ツールバーの[表示]-[その他のウィンドウ]-[F# Interactive]で開くことができます。F# Interactiveは開かれているプロジェクトとは別に独立して機能します。

VS2010 F# プロジェクト
VS2010 F# プロジェクト

 貼り付けた部分を強調選択して、[Alt]+[Enter]を押すことで、対話的に部分的コードの確認ができます。

VS2010 F# Interactive
VS2010 F# Interactive

 再帰関数factoの定義文をF# Interactiveにて送信してみると下記のようになります。

[リスト6]再帰関数factoの定義
 >let rec facto n =  if n <= 1 then 1 else n * facto (n-1) ;;

 ▼

val facto : int -> int

 同様にprintfの行もコピーしてF# Interactiveを実行すると詳細が確認できます。F# Interactiveでデータが戻ってくる際に送信したデータが最下行に表示され、実行内容がその上に表示されるのは若干紛らわしいですが、内容は理解できると思います。

VS2010 F# Interactiveの結果表示
VS2010 F# Interactiveの結果表示

 再帰関数factoの呼び出しをF#Interactiveにて実行すると下記のようになります。

[リスト7]factoの呼び出し
 >facto 5 ;;

 ▼

val it : int = 120

 問題ない場合には、[ビルド]-[TestApp(プロジェクト名)]-[デバッグ]-[デバッグなしで開始]を実行してください。この作業はC#でも同様ですので、解説は省きます。

 上記のリスト6は、数学の書き方であらわすと、n! です。関数名factoの前にあるrecというキーワードですが、定義する関数が再帰関数の場合、関数名の前にrecキーワードを必ずつける必要があります。

 例えば5の階乗を求める場合、1*2*3*4*5でもよいのですが、この場合、再帰関数を使用した方が式が簡潔で美しくなります。n!はn * (n-1)!と同じですので、(n-1)をパラメータとして、関数factoをnが1になるまで繰り返します。つまり、計算は下記のようなプロセスになります。

[リスト8]facto 5の計算
facto 5
= 5 * facto 4
= 5 * (4 * facto 3)
= 5 * (4 * (3 * facto 2))
= 5 * (4 * (3 * (2 * facto 1)))  // n <= 1 なので、facto 1に1が返される
= 5 * (4 * (3 * (2 * 1)))
= 5 * (4 * (3 * 2))
= 5 * (4 * 6)
= 5 * 24
= 120

 再帰関数の基本として、常に頭に入れておきたいポイントが2つあります。

  • 再帰が終了する条件があること
  • パラメーターが再帰が終了する条件にいつか必ず到着すること

 一般的には、パラメーターなどのカウンターの値を減少させていき、再帰の終了する条件を「カウンターが0もしくは1になるまで」とするパターンがよく見られます。上記のリスト8もこのパターンです。

[構文]再帰関数
let rec 関数名 パラメーターリスト =
    関数本体(function-body)

相互再帰関数

 再帰関数でよく見かけられる機能で、相互再帰関数という関数があります。再帰関数の一種で、2つ以上の関数がお互いを呼び出す再帰関数のことです。

[構文]相互再帰関数
let rec 関数名1 パラメーターリスト =
    関数本体1(function1-body)
and 関数名2 パラメーターリスト =
    関数本体 2(function2-body)

 下記のリスト9はnが奇数か偶数かを判別する相互再帰関数です。パフォーマンスが下がる、デバッグしにくいなどの理由で避けたい機能としても知られていますが、個々に複数の関数を宣言するより関数を概念化しやすい、コードがすっきり見えるなどの理由で好むプログラマーもいるようです。

[リスト9]相互再帰関数の定義
 > let rec even n =  (n = 0us) || odd (n - 1us)
and odd n = (n <> 0us) && even(n - 1us) ;;

 ▼

val even : uint16 -> bool
val odd : uint16 -> bool
[リスト10]関数even の呼び出し
> even 5us ;;

 ▼

val it : bool = false
[リスト11]関数odd の呼び出し
> odd 5us ;;

 ▼

val it : bool = true

 n = 0usがtrue、もしくは、odd(n - 1us) がtrueの場合、関数evenはtrueを返します。数字のあとにある"us"はその値が16ビット符号なし整数(uint16型)であることをあらわします。両方がfalseの場合にはfalseを返します。関数oddはn<>0us(パラメーターn<>符号なし0)がtrueで、かつ、even(n - 1us)がtrueの場合のみtrueを返し、それ以外の場合には、falseを返します。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

  • WINGSプロジェクト 星山 仁美(ホシヤマ ヒトミ)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂...

バックナンバー

連載:C#プログラマのためのF#入門

もっと読む

All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5