スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#入門基本編落穂拾い その37 制約(1) equality

一般に型推論に関数の型の決定をまかせると、その関数が定義できる一番広い形で、型を決定してくれます。
 
まずは次の関数定義をみてください。 
 
> let isEq x y =  
    x = y ;;           
 
val isEq : 'a -> 'a -> bool when 'a : equality
 
ここでは関数isEqの型が'a -> 'a -> bool when 'a : equality
となっています。この、when 'a : equalityの部分は型'aに対する制約を表し、ここでは型'aが二つの要素について等しいかどうか決定可能でなければならないということを示しています。
(なお制約は、推論により自動的に設定されますし、自分で明示的に付け加えることもできます。)
 
F#の基本の型(int,string)等については、equalityが定義されており、値型(int等)でも参照型(string等)でも、値が等しいときに、等しいとされます。
 
> let t0 = "abc";;
val t0 : string = "abc"
 
> let t1 = "abc";;
val t1 : string = "abc"
 
> t0 = t1;;
val it : bool = true
 
なおリスト、タプル、レコード、Discriminated unionにもequalityが定義されており、
リストは要素数が等しく、それぞれの対応する要素が等しい。
タプルもリストと同様。
レコードは各フィールドの値が等しい
Discriminated unionはタグ及びそれに付随する値が等しい
ときに等しいとされます。
なおリストの中にリストがあるとかというようなネストされた値の場合には、どんどんネストされた内部を同様に等しいかどうかを調べていくようになっています。
 

 
> let t0 = [1;2];;
val t0 : int list = [1; 2]
 
> let t1 = [1;2];;
val t1 : int list = [1; 2]
 
> t0 = t1;;
val it : bool = true
 

 
> let t0 = ([1;2],(1,2));;
val t0 : int list * (int * int) = ([1; 2], (1, 2))
 
> let t1 = ([1;2],(1,2));;
val t1 : int list * (int * int) = ([1; 2], (1, 2))
 
> t0 = t1;;
val it : bool = true
 
さてやっかいなのはオブジェクトのequalityです。(ただしリスト、タプル、レコード、Discriminated unionについては、内部的にオブジェクトとして実装されていますが、上でやったように、構成要素が基本的型(int,string等)の場合は単に値が等しいかどうかというように処理できます。)
なにが厄介かというと、オブジェクトのequalityを考えるときは、識別子が指しているメモリの場所が等しい(reference equalityといいます。)か、オブジェクトとして内部的に値として保持している値が等しい(structural equalityといいます。)という二通りの等価性が考えられるからです。
ですから、string,リスト、タプル、レコード、Discriminated unionについて構成要素が基本的型の場合は、structural equalityが使用されるということになります。
 
それでは例を一つ
 
> type MyClass (x:int) =
     member this.Val =
        x  ;;
 
> let t0 = new MyClass(3);;
val t0 : MyClass
 
> let t1 = new MyClass(3);;
val t1 : MyClass
 
> t0 = t1;;
val it : bool = false
 
ということで、t0,t1はメモリの違う部分に保持されていますので、内部的に保持する値は共に3ですが、異なるものとして評価されます。
 
(メモリの同じ部分に保持されているかとうかはSystem.Object.ReferenceEqualsを用いて調べることができます。
> System.Object.ReferenceEquals(t0,t1);;
val it : bool = false )
 
これをstructural equalityにするには、Equalsメソッドをオーバーライドする必要があります。
 
type MyClass (x:int) =
     member this.Val =
        x
     override this.Equals(o :obj) =
        match o with
        | :? MyClass as other -> (other.Val = this.Val)
        | _ -> false //型が違う場合
 
 
> let t0 = new MyClass(3);;
val t0 : MyClass
 
> let t1 = new MyClass(3);;
val t1 : MyClass
 
> t0 = t1;;
val it : bool = true
 
(オブジェクトのメンバーがさらに、オブジェクトだった場合とかは、さらに参照先の値を手繰り寄せて比較するということが必要になってきます。)
 
なお上のように定義すると
warning FS0346: The struct, record or union type 'MyClass' has an explicit implementation of 'Object.Equals'. Consider implementing a matching override for 'Object.GetHashCode()'
 
というように警告がでますが、Object.GetHashCode()については後日紹介したいと思います。
 
(備考)unit型にもequalityは定義されています。
> () = ();;
val it : bool = true
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

関数isMoreThan

>関数isMoreThanの型
次の記事にならないとでてこない。

No title

訂正しました。御指摘ありがとうございました。
プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。