関数型言語「F#」一歩目

F#(F Sharp)を少し触ったので、メモしておきます。

F#とは

WikiPediaによると

F#はマイクロソフトの研究チームによる.NETプラットフォーム向けの関数型プログラミング言語である。型安全・オブジェクト指向であり、型推論の機能をもつ。 他の.NET言語と同様に.NETクラスライブラリを利用したり作成したりすることができる。

とのことで、ML→OCamlの流れをくむようです。次期Visual Studio2010にも標準搭載される予定です。

F#のメリット

MSDNの記事にある関数型言語のメリットは次の通り。

マルチコアCPUが普及するにつれて、安全な並列プログラムの記述が大きな関心事となっています。関数型言語では、開発者による同時実行のサポートを支援するため、不変データ構造が推奨されます。このような構造は、スレッドセーフやアトミックアクセスについて心配することなく、スレッド間やマシン間で渡すことができます。

例としては、XSLTを使ってXMLを解析することが挙げられています。

F#のインストール

最新版は1.9.6.2CTP(コミュニティテクノロジプレビュー)で、Microsoft Researchからインストーラをダウンロードできます。


インタラクティブシェル

インストールが終わると、プログラムメニューに「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資料が分かりやすいです。