メソッド

前回でクラスにフィールドを追加する方法を学んだので 今度はクラスに関数を持たせる方法を説明します。 以前述べたとおり、クラスの持つ関数のことを メソッドと呼びます。 メソッドの定義に相当する構文規則を抜き出したのが 次の部分です 参考
メソッドの定義
// Instance method definition. [ attributes ] member [inline] self-identifier.method-name parameter-list [ : return-type ] = method-body // Static method definition. [ attributes ] static member [inline] method-name parameter-list [ : return-type ] = method-body // Abstract method declaration or virtual dispatch slot. [ attributes ] abstract member method-name : type-signature // Virtual method declaration and default implementation. [ attributes ] abstract member method-name : type-signature [ attributes ] default self-identifier.method-name parameter-list [ : return-type ] = method-body // Override of inherited virtual method. [ attributes ] override self-identifier.method-name parameter-list [ : return-type ] = method-body // Optional and DefaultParameterValue attributes on input parameters [ attributes ] [ modifier ] member [inline] self-identifier.method-name ([] input) [ : return-type ]
まずは、最も基本的なインスタンスメソッドをみてみます(一番上の形式)。
メソッドの定義
type CM(x:int,y:int) = class
    member val px = x with get,set
    member val py = y with get,set
    member this.length() = sqrt(float(this.px*this.px+this.py*this.py))
    member this.dist() = abs(this.px) + abs(this.py)
    member this.distTo x y = abs(this.px-x) + abs(this.py-y)
    member this.print() = printfn "x=%d,y=%d" this.px this.py
    end;;

let cm = CM(3,4);
printfn "%f" (cm.length());
printfn "%d" (cm.dist());
printfn "%d" (cm.distTo 10 20);
cm.print();;
メソッドの定義(軽量構文)
type CM(x:int,y:int) =
    member val px = x with get,set
    member val py = y with get,set
    member this.length() = sqrt(float(this.px*this.px+this.py*this.py))
    member this.dist() = abs(this.px) + abs(this.py)
    member this.distTo x y = abs(this.px-x) + abs(this.py-y)
    member this.print() = printfn "x=%d,y=%d" this.px this.py

let cm = CM(3,4)
printfn "%f" (cm.length())
printfn "%d" (cm.dist())
printfn "%d" (cm.distTo 10 20)
cm.print()
member self-identifier.メソッド名 パラメータ 関数本体でインスタンスメソッドを宣言します。 self-identifierはthisでもselfでもxでもなんでも構いません。 memberキーワードとself-identifier.があることを除けば これは通常の関数定義と変わりはありません。 もちろん、メンバ関数はカリー化することも出来るし 別の変数に代入することも出来ます そして、関数呼び出しは、オブジェクト名.メソッド名とします。 self-identifierというのは関数の定義の本体から 自クラス内で定義されているメンバにアクセスするためのもので 通常はxやvなどの短い名前か thisやselfといった名前を用います。 この例ではthisとなっているため this.pxとすることでpxメンバの値を参照することが出来ます。

静的メソッド(staticなメソッド)

メソッドは基本的にオブジェクトを通じて呼び出しますが staticキーワードをつけて宣言した場合 クラスを通して呼び出すことが出来ます。 そのため、関数を定義する際に self-identifierをつける必要はありません。 これは、例えば数学用のクラスのような 具体的なオブジェクトが必要ないようなクラスで よく利用されます。 次の例では先ほどのクラスに追加で、 任意の二点間の単純な和で求める距離を求める静的メソッドdistanceを定義しています。
静的メソッド
type CM(x:int,y:int) = class
    member val px = x with get,set
    member val py = y with get,set
    static member distance x y = abs(x)+abs(y) //この行が増えた
    member this.length() = sqrt(float(this.px*this.px+this.py*this.py))
    member this.dist() = abs(this.px) + abs(this.py)
    member this.distTo x y = abs(this.px-x) + abs(this.py-y)
    member this.print() = printfn "x=%d,y=%d" this.px this.py
    end;;

printfn "distance=%d" (CM.distance 10 10);;
静的メソッド(軽量構文)
type CM(x:int,y:int) =
    member val px = x with get,set
    member val py = y with get,set
    static member distance x y = abs(x)+abs(y) //この行が増えた
    member this.length() = sqrt(float(this.px*this.px+this.py*this.py))
    member this.dist() = abs(this.px) + abs(this.py)
    member this.distTo x y = abs(this.px-x) + abs(this.py-y)
    member this.print() = printfn "x=%d,y=%d" this.px this.py

printfn "distance=%d" (CM.distance 10 10)

このように、staticなメソッドを呼び出す際は クラス名.関数名とすることで呼び出すことが出来ます。 構文の3番目から5番目の形式はオブジェクト指向の継承とかかわってくるので、 オーバーライドのページで解説します。

オブジェクトとしてのレコード

レコードはフィールドの他にメソッドも持つことが出来ます。 これにより、レコードを用いて オブジェクトをエミュレートすることが出来ます。 レコードにメソッドを定義するには次の構文を用います。 メソッド名の前の型名は、 クラスの時と同じく自分自身を参照するための名前です。
メンバ関数付きレコードの定義
type 型名 = { フィールド名1 : 型名1; ... ; フィールド名n : 型名n } with member 型名1.メンバ関数名1 パターン1 ... パターンn = 本体 .. member 型名n.メンバ関数名n パターン1 ... パターンn = 本体 end
これを用いて、 点の座標を表すレコードを作成してみます。
Point型の利用
type Point = {mutable x : int; mutable y : int}
    with
        member v.move (p:Point) =
            v.x <- v.x + p.x;
            v.y <- v.y + p.y;
        member v.show _=
            printfn "%s" ("x=" + v.x.ToString() + ",y=" + v.y.ToString())
    end;;
let p = {x=10;y=10} in
let move_vector = {x=100;y=100} in
p.show();
p.move(move_vector);
p.show();;
Point型の利用(軽量構文)
type Point = {mutable x : int; mutable y : int}
    with
        member v.move (p:Point) =
            v.x <- v.x + p.x
            v.y <- v.y + p.y
        member v.show _=
            printfn "%s" ("x=" + v.x.ToString() + ",y=" + v.y.ToString())
let p = {x=10;y=10}
let move_vector = {x=100;y=100}
p.show()
p.move(move_vector)
p.show()
Point型はxとyの値を持つだけの単純なデータ型で フィールドの値を表示するshowメソッドと 別のPoint型を(移動するベクトル量として)受け取るmoveメソッドを持っています。 この例の通り、メソッドへのアクセスは フィールドへのアクセスと同じくドット記法を用います。