かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

手軽なスクリプト言語としてのF# その8「レコード」

8回目です。次はレコードというやつをやってみようと思います。ここを見てる人はC#をやったことがあるという前提でエントリ書いてるのですが、C#でいう単純なプロパティを持ったクラスです。メソッドとかも持たせようと思えば持たせることが出来ます。

定義方法は簡単で以下のような感じになります。

type レコード名 = { フィールド名 : フィールドの型;フィールド名 : フィールドの型;フィールド名 : フィールドの型;... }

フィールドの定義は複数行にわけて書くことも出来ます。フィールドの定義を1行に1フィールドにする場合はセミコロン省略で書いてOKです。

type レコード名 = {
    フィールド名 : フィールドの型
    フィールド名 : フィールドの型
    フィールド名 : フィールドの型
     ....
    }

ということでさくっと名前と年齢を持つ人間を表すレコードを作ってみようと思います。

// Personレコードの定義
type Person = { Name : string; Age : int }

// Personレコードの作成
let p = { Name = "田中"; Age = 48 }
// こんな風にレコード名を明示することも出来る
let p2 = { Person.Name = "大田"; Age = 29 }

// レコードのフィールドの値は.を使ってアクセスできる
printfn "%s さんは %d 歳です" p.Name p.Age
printfn "%s さんは %d 歳です" p2.Name p2.Age

実行結果は以下のようになります。

田中 さんは 48 歳です
大田 さんは 29 歳です

レコードの作り方が特徴的ですね。 { フィールド名 = 値; ... }で作れる上に、フィールドの名前と型あたりからF#が必至に空気を読んで、どのレコードか探してきてくれるのが印象的です。まったく同じフィールドを持つレコードがある時に備えてp2でやってるようにレコード名を明示する方法もあります。


さらに、レコードの値をコピーする方法も用意されています。 { 元になるレコード with フィールド名 = 値; ... }のような形になります。これも小さなプログラムで動きを確認してみましょう。

// Personレコードの定義
type Person = { Name : string; Age : int }

// Personレコードの作成
let p = { Name = "田中"; Age = 48 }

// pの値をもとに値の変わるフィールドだけ設定しなおす
let newTanaka = { p with Age = 100 }

// 印字
printfn "%s さんは %d 歳です" p.Name p.Age
printfn "%s さんは %d 歳です" newTanaka.Name newTanaka.Age

pの値をもとにnewTanakaを作ってます。実行結果は以下のようになります。newTanakaで再定義したAgeの値が反映されてるのがわかると思います。

田中 さんは 48 歳です
田中 さんは 100 歳です

さて、次はレコードにメソッドを定義してみようと思います。メソッドの定義の仕方は以下のような感じで出来ます。

type レコード名 = { フィールドの定義 } with 
    member 自己識別子.メソッド名(引数) =
        処理の中身
    member 自己識別子.メソッド名(引数) =
        処理の中身

自己識別子ってなんぞや?という感じですが、C#でいうところのthisに該当するものです。F#ではすきに名前を付けることができます。早速、自己紹介をするメソッドを追加してみようと思います。

// Personレコードの定義
type Person = { Name : string; Age : int } with
    member this.Greet() =
        printfn "私の名前は %s で %d 歳です" this.Name this.Age

// Personレコードの作成
let p = { Name = "田中"; Age = 48 }

// メソッドを呼んでみる
p.Greet()

今回は引数を受け取らないメソッドを定義していますが、タプルを渡す関数と同じ要領で複数の値を渡すことができます。

なんか段々オブジェクト指向ちっくになってきました。