メソッド
前回でクラスにフィールドを追加する方法を学んだので
今度はクラスに関数を持たせる方法を説明します。
以前述べたとおり、クラスの持つ関数のことを
メソッドと呼びます。
メソッドの定義に相当する構文規則を抜き出したのが
次の部分です
参考
- メソッドの定義
-
// 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メソッドを持っています。
この例の通り、メソッドへのアクセスは
フィールドへのアクセスと同じくドット記法を用います。