レコード (F#)

レコードは、名前付きの値の単純な集合を表しており、オプションでメンバーを含みます。

[ attributes ]
type [accessibility-modifier] typename = { 
    [ mutable ] label1 : type1;
    [ mutable ] label2 : type2;
    ...
    }
    member-list

解説

上の構文で、typename はレコード型の名前です。label1 および label2 は、値の名前であり、ラベルと呼ばれます。type1 および type2 は、これらの値の型です。member-list は、型のメンバーのリストで、省略することもできます。

次にいくつかの例を示します。

type Point = { x : float; y: float; z: float; }
type Customer = { First : string; Last: string; SSN: uint32; AccountNumber : uint32; }

各ラベルが独立した行にある場合、セミコロンは省略可能です。

値は、レコード式と呼ばれる式で設定できます。コンパイラは、使用されているラベルから型を推測します (ラベルが他のレコード型のラベルと十分に区別される場合)。レコード式は中かっこ ({ }) で囲みます。次のコードは、x、y、および z というラベルが付いた 3 つの float 要素を持つレコードを初期化するレコード式を示しています。

let mypoint = { x = 1.0; y = 1.0; z = -1.0; }

同じラベルを持つ別の型が存在する可能性がある場合は、短縮形を使用しないでください。

type Point = { x : float; y: float; z: float; }
type Point3D = { x: float; y: float; z: float }
// Ambiguity: Point or Point3D?
let mypoint3D = { x = 1.0; y = 1.0; z = 0.0; }

最も新しく宣言された型のラベルは、以前に宣言された型のラベルよりも優先されます。そのため、上の例では、mypoint3D は Point3D と推論されます。次のコードに示すように、レコード型を明示的に指定できます。

let myPoint1 = { Point.x = 1.0; y = 1.0; z = 0.0; }

クラス型の場合と同様に、レコード型に対してメソッドを定義できます。

レコード式を使用したレコードの作成

レコード内で定義されているラベルを使用して、レコードを初期化できます。この操作を行う式を、レコード式といいます。中かっこを使用してレコード式を囲み、セミコロンを区切り記号として使用します。

レコードを作成する方法の例を次に示します。

type MyRecord = {
    X: int;
    Y: int;
    Z: int 
    }

let myRecord1 = { X = 1; Y = 2; Z = 3; }

レコード式および型定義の最後のフィールドの後のセミコロンは、すべてのフィールドが 1 行に含まれるかどうかにかかわらず、省略可能です。

レコードを作成する場合は、各フィールドの値を指定する必要があります。フィールドで、初期化式中の他のフィールドの値を参照することはできません。

次のコードでは、myRecord2 の型がフィールドの名前から推論されます。オプションで、型の名前を明示的に指定できます。

let myRecord2 = { MyRecord.X = 1; MyRecord.Y = 2; MyRecord.Z = 3 }

レコード構築のもう 1 つの形式は、既存のレポートをコピーし、必要に応じてフィールド値の一部を変更する必要がある場合に便利です。次のコード行は、これを示したものです。

let myRecord3 = { myRecord2 with Y = 100; Z = 2 }

この形式のレコード式を、レコード式のコピーおよび更新といいます。

レコードは既定で変更不可ですが、コピーおよび更新の式を使用して、変更されたレコードを簡単に作成できます。また、変更可能なフィールドを明示的に指定できます。

type Car = {
    Make : string
    Model : string
    mutable Odometer : int
    }
let myCar = { Make = "Fabrikam"; Model = "Coupe"; Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21

DefaultValue 属性は、レコードのフィールドを使用しないでください。既定値に初期化されるフィールドを持つレコードの既定のインスタンスを定義およびコピーを使用して、既定値とは異なる任意のフィールドを設定するのには、レコード式を更新するより良いアプローチです。

// Rather than use [<DefaultValue>], define a default record.
type MyRecord =
    { 
        field1 : int 
        field2 : int
    }

let defaultRecord1 = { field1 = 0; field2 = 0 }
let defaultRecord2 = { field1 = 1; field2 = 25 }

// Use the with keyword to populate only a few chosen fields
// and leave the rest with default values.
let rr3 = { defaultRecord1 with field2 = 42 }

レコードを使用したパターン マッチ

パターン マッチでレコードを使用できます。いくつかのレコードを明示的に指定し、一致が生じた場合に割り当てられる他のフィールドに対する変数を指定できます。これを次のコード例に示します。

type Point3D = { x: float; y: float; z: float }
let evaluatePoint (point: Point3D) =
    match point with
    | { x = 0.0; y = 0.0; z = 0.0 } -> printfn "Point is at the origin."
    | { x = xVal; y = 0.0; z = 0.0 } -> printfn "Point is on the x-axis. Value is %f." xVal
    | { x = 0.0; y = yVal; z = 0.0 } -> printfn "Point is on the y-axis. Value is %f." yVal
    | { x = 0.0; y = 0.0; z = zVal } -> printfn "Point is on the z-axis. Value is %f." zVal
    | { x = xVal; y = yVal; z = zVal } -> printfn "Point is at (%f, %f, %f)." xVal yVal zVal

evaluatePoint { x = 0.0; y = 0.0; z = 0.0 }
evaluatePoint { x = 100.0; y = 0.0; z = 0.0 }
evaluatePoint { x = 10.0; y = 0.0; z = -1.0 }

このコードによって、次のような出力が生成されます。

Point is at the origin.
Point is on the x-axis. Value is 100.000000.
Point is at (10.000000, 0.000000, -1.000000).

レコードとクラスの違い

レコードのフィールドは、自動的にプロパティとして公開され、レコードの作成およびコピーで使用される点が、クラスとは異なります。また、レコードの作成もクラスの作成とは異なります。レコード型では、コンストラクターを定義できません。代わりに、このトピックで説明した構築構文が適用されます。クラスでは、コンストラクターのパラメーター、フィールド、およびプロパティの間に直接的な関係がありません。

共用体型および構造体型のように、レコードには構造的な等値セマンティクスがあります。クラスには、参照が等値であるセマンティクスがあります。ドメイン スコープを追加するコード例を次に示します。

type RecordTest = { X: int; Y: int }
let record1 = { X = 1; Y = 2 }
let record2 = { X = 1; Y = 2 }
if (record1 = record2) then
    printfn "The records are equal."
else
    printfn "The records are unequal."

クラスを使用して同じコードを作成した場合は、2 つの値がヒープ上の 2 つのオブジェクトを表し、アドレスのみが比較されるため (クラス型が System.Object.Equals メソッドをオーバーライドする場合を除く)、2 つのクラス オブジェクトは等しくなりません。

参照

関連項目

クラス (F#)

パターン マッチ (F#)

その他の技術情報

F# の型

F# 言語リファレンス