関数型言語「F#」一歩目
F#(F Sharp)を少し触ったので、メモしておきます。
F#とは
WikiPediaによると
F#はマイクロソフトの研究チームによる.NETプラットフォーム向けの関数型プログラミング言語である。型安全・オブジェクト指向であり、型推論の機能をもつ。 他の.NET言語と同様に.NETクラスライブラリを利用したり作成したりすることができる。
とのことで、ML→OCamlの流れをくむようです。次期Visual Studio2010にも標準搭載される予定です。
F#のメリット
マルチコアCPUが普及するにつれて、安全な並列プログラムの記述が大きな関心事となっています。関数型言語では、開発者による同時実行のサポートを支援するため、不変データ構造が推奨されます。このような構造は、スレッドセーフやアトミックアクセスについて心配することなく、スレッド間やマシン間で渡すことができます。
インタラクティブシェル
インストールが終わると、プログラムメニューに「Microsoft F# CTP 1.9.6.2」と表示されるので、「F# Interactive (Console)」をクリックして、F#のインタラクティブコンソール(fsi.exe)を起動します。
ヘルプで表示されるメッセージは次の通り
#r "file.dll";; DLLの参照追加
#I "path";; 参照するDLL群のパスの追加
#load "file.fs" ...;; ファイルの読み込み
#time;; 実行時刻のON/OFF切り替え
#help;; ヘルプの表示.
#quit;; 終了
例えば、DLLを読み込むコマンドは次のような感じです。
#I @"C:\Sample\bin\Debug";;
#r "FSharpSample.dll";;
open MyNameSapace;;
#quit;;
VS2008での実行
インタラクティブシェルは終了して、Visual Studio 2008を起動します。新規プロジェクトに次のテンプレートが表示されるのでチュートリアル(F# Tutorial)を選択します。
なお、VS2008がなくても、SharpDevelop、もしくは、Visual Studio Shellを使えばF#をIDEで開発できます。
F#のチュートリアル
生成されるチュートリアルのコードはインタラクティブではなく、コンパイル(fsc.exeでアセンブリを生成)して実行します。
チュートリアルで生成されるコードは次の通りです(訳は適当)。
// F#の様々なサンプルはこちら: // http://go.microsoft.com/fwlink/?LinkID=124614 // // コンテンツ: // - 簡単な計算 // - 整数の関数 // - タプル(組) // - 真偽値 // - 文字列 // - リスト // - 配列 // - コレクション // - 関数 // - 型: 共用型 // - 型: レコード型 // - 型: クラス // - 型: インターフェイス // - 型: インターフェイスを実装したクラス // - 出力 // 簡略構文を使うモード(コンパイラ ディレクティブ) #light // System名前空間の使用 open System // 簡単な計算 // --------------------------------------------------------------- // 幾つかの計算の例です。 // メモ:'///'のコメントは変数を参照した時にホバー表示されます。 /// シンプルな数値の定数 let int1 = 1 /// 2つ目のシンプルな数値の定数 let int2 = 2 /// 2つの値の足し算 let int3 = int1 + int2 // 数値の関数 // --------------------------------------------------------------- /// 数値を使う関数を宣言 let f x = 2*x*x - 5*x + 3 /// 関数の結果 let result = f (int3 + 4) /// 簡単な関数 let increment x = x + 1 /// 再帰による階乗計算する関数 let rec factorial n = if n=0 then 1 else n * factorial (n-1) /// 2つの数の最大公約数を計算 let rec hcf a b = if a=0 then b elif a<b then hcf a (b-a) else hcf (a-b) b // 注: 2つの引数はスペース区切り // メモ: 関数の引数は通常はスペース区切り // メモ: 'let rec' 定義は再帰関数 hcf 24 18 // 結果→int = 6 // タプル(組) // --------------------------------------------------------------- // 数値のシンプルなタプル let pointA = (1, 2, 3) // 整数、文字列、浮動小数点のシンプルなタプル let dataB = (1, "fred", 3.1415) /// タプル内の2つの値を入れ替える関数 let Swap (a, b) = (b, a) // 真偽値 // --------------------------------------------------------------- /// シンプルな真偽値 let boolean1 = true /// 2つ目のシンプルな真偽値 let boolean2 = false /// 「かつ、または、否定」を用いて真偽値を判定 let boolean3 = not boolean1 && (boolean2 || false) // 文字列 // --------------------------------------------------------------- /// シンプルな文字列 let stringA = "Hello" /// 2つ目のシンプルな文字列 let stringB = "world" /// 文字列の結合を用いて"Hello world" let stringC = stringA + " " + stringB /// .NETライブラリの機能を使って"Hello world" let stringD = String.Join(" ",[| stringA; stringB |]) // タブキーでインテリセンスを試してみてください // リスト // --------------------------------------------------------------- /// 空のリスト let listA = [ ] /// 3つの数値を格納するリスト let listB = [ 1; 2; 3 ] /// 3つの数値を格納するリスト。 ::はリスト連結のオペレータ let listC = 1 :: [2; 3] /// 再帰関数を使って数値のリストの合計を計算 let rec SumList xs = match xs with | [] -> 0 | y::ys -> y + SumList ys /// リストの合計 let listD = SumList [1; 2; 3] /// 1から10までをすべて含んだリスト let oneToTen = [1..10] /// 10の2乗 let squaresOfOneToTen = [ for x in 0..10 -> x*x ] // 書き換え可能な配列 // --------------------------------------------------------------- /// 配列を作成 let arr = Array.create 4 "hello" arr.[1] <- "world" arr.[3] <- "don" // 結果→string [] = [|"hello"; "world"; "hello"; "don"|] /// 配列オブジェクトのインスタンスメソッドを使って長さを取得 let arrLength = arr.Length // スライスノーテーションを使って、サブ配列を抽出 let front = arr.[0..2] // コレクション // --------------------------------------------------------------- /// 数値のキーと文字列の値を持つディクショナリ let lookupTable = dict [ (1, "One"); (2, "Two") ] let oneString = lookupTable.[1] // 以下、他の主要なデータ構造: // System.Collections.Generic // Microsoft.FSharp.Collections // Microsoft.FSharp.Collections.Seq // Microsoft.FSharp.Collections.Set // Microsoft.FSharp.Collections.Map // 関数 // --------------------------------------------------------------- /// 入力値を2乗する関数 let Square x = x*x // 値のリストを関数にマップ let squares1 = List.map Square [1; 2; 3; 4] let squares2 = List.map (fun x -> x*x) [1; 2; 3; 4] // パイプライン // (|>)のこと。結果を次へ渡す let squares3 = [1; 2; 3; 4] |> List.map (fun x -> x*x) let SumOfSquaresUpTo n = [1..n] |> List.map Square |> List.sum // 型:共用型 // --------------------------------------------------------------- type Expr = | Num of int | Add of Expr * Expr | Mul of Expr * Expr | Var of string let rec Evaluate (env:Map<string,int>) exp = match exp with | Num n -> n | Add (x,y) -> Evaluate env x + Evaluate env y | Mul (x,y) -> Evaluate env x * Evaluate env y | Var id -> env.[id] let envA = Map.of_list [ "a",1 ; "b",2 ; "c",3 ] let expT1 = Add(Var "a",Mul(Num 2,Var "b")) let resT1 = Evaluate envA expT1 // 型: レコード型 // --------------------------------------------------------------- type Card = { Name : string; Phone : string; Ok : bool } let cardA = { Name = "Alf" ; Phone = "(206) 555-8257" ; Ok = false } let cardB = { cardA with Phone = "(206) 555-4112"; Ok = true } let ShowCard c = c.Name + " Phone: " + c.Phone + (if not c.Ok then " (unchecked)" else "") // 型: クラス // --------------------------------------------------------------- /// 2次元ベクタクラス type Vector2D(dx:float, dy:float) = let length = sqrt(dx*dx + dy*dy) member v.DX = dx member v.DY = dy member v.Length = length member v.Scale(k) = Vector2D(k*dx, k*dy) // Vector2Dというクラス // コンストラクタは、dxとdyという浮動小数点型の引数2つ // lengthはフィールドのようなもの // memberで始まっている行がメンバ(フィールド、メソッド、プロパティ) // DX、DY、Lengthはプロパティのようなもの // Scaleはkという引数を取るメソッドのようなもの // 型: インターフェイス // --------------------------------------------------------------- type IPeekPoke = abstract Peek: unit -> int abstract Poke: int -> unit // 型: インターフェイスを実装したクラス // --------------------------------------------------------------- /// つつかれた回数を保存するウィジェット type Widget(initialState:int) = /// ウィジェットの内部状態 let mutable state = initialState // IPeekPokeインターフェイスの実装 interface IPeekPoke with member x.Poke(n) = state <- state + n member x.Peek() = state member x.HasBeenPoked = (state <> 0) let widget = Widget(12) :> IPeekPoke widget.Poke(4) let peekResult = widget.Peek() // 出力 // --------------------------------------------------------------- // 数値の出力 printfn "peekResult = %d" peekResult // 一般的な出力のために%Aを使って結果を出力 printfn "listC = %A" listC
上記のチュートリアルのを、インタラクティブシェルで実際に入力する(行末に;;必要)と理解しやすいと思いました。
基本文法を理解するにはTechDaysのPPTX資料が分かりやすいです。
- 参考
- TechDays2009 F# 入門(パワポ、動画)
- MSDN .NET Framework で関数型プログラミング手法を使用する
- F#入門
- F#で学ぶ関数型プログラミング入門
- .NET開発者向け新関数型言語 「F#」入門
- いげ太のブログ