かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

手軽なスクリプト言語としての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()

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

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