スポンサーサイト

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

F#入門第37回(OOP(2)暗黙的クラス定義(2))

今回のお題は「OOP(2)暗黙的クラス定義(2)」です。
 
まずは宿題の解答例からです。
(宿題)
type Vec2D (x : float,y:float) =
 
    let len = 
        let  sqr r = r * r
        sqrt <| sqr x + sqr y
    
    member this.ShowContents
                = printfn "x = %f y = %f" x y
    member this.X = x
    member this.Y = y
    member this.Length = len
    member this.Add (t :Vec2D) =
            new Vec2D(x + t.X,y + t.Y)
 
において、ベクトルの成分がともに0.0であるときtrueを返し、それ以外のときfalseを返すプロパティisZeroと、float*float型の引数をとり、それぞれをx成分、y成分に足した、Vec2D型の値をかえす、メソッドadd2を追加してください。
解答例は次のようになります。
 
type Vec2D (x : float,y:float) =
 
    let len = 
            let  sqr r = r * r
            sqrt <| sqr x + sqr y
     
    member this.ShowContents
                = printfn "x = %f y = %f" x y 
                  
    member this.X = x
    member this.Y = y
    member this.Length = len
    member this.Add (t :Vec2D) =
            new Vec2D(x + t.X,y + t.Y)
    member this.IsZero = 
            (x = 0.0) && (y = 0.0)
    member this.Add2 (ax,ay) =
            new Vec2D(x+ax,y+ay)
 
実行例
 
> let v1 = new Vec2D (0.0,0.0);;
val v1 : Vec2D
 
> v1.IsZero ;;
val it : bool = true
 
> let v2 = new Vec2D (1.0,3.0);;
val v2 : Vec2D
 
> v2.IsZero;;
val it : bool = false
 
> let v3 = v2.Add2(4.0,2.0);;
val v3 : Vec2D
 
> v3.ShowContents;;
x = 5.000000 y = 5.000000
val it : unit = ()
 
さて、今回はまず最初に複数のコンストラクタの定義方法を紹介します。
暗黙的なクラス定義の場合にはPrimary Constructorsから、自動的にメインのコンストラクタが、自動生成されます。追加のコンストラクタを定義する場合には,そのコンストラクタの定義の最後でこのメインコンストラクタを呼び出すようにすることがポイントです。

type Vec2D (x : float,y:float) =
 
    let len = 
            let  sqr r = r * r
            sqrt <| sqr x + sqr y
     
    member this.ShowContents
                = printfn "x = %f y = %f" x y 
                  
    member this.X = x
    member this.Y = y
    member this.Length = len
    
    new () = new Vec2D (0.0,0.0)   //これが追加のコンストラクタ
 
実行例
 
> let t = new Vec2D ();;
val t : Vec2D
 
> t.ShowContents;;
x = 0.000000 y = 0.000000
val it : unit = ()
 
次は、int*int型のタプルをとって、それによりインスタンスを生成するコンストラクタを更に追加してみたいと思います。
F#の場合、タプル内の要素の個数が同じコンストラクタを複数定義するとき、オーバーロード属性というのを、追加してやる必要があります。これは、定義の直前の行に[<OverloadID("自分で設定する識別用文字")>]を追加します。
例えば次のようになります。
 
type Vec2D (x : float,y:float) =
 
    let len = 
            let  sqr r = r * r
            sqrt <| sqr x + sqr y
     
    member this.ShowContents
                = printfn "x = %f y = %f" x y 
                  
    member this.X = x
    member this.Y = y
    member this.Length = len
    
    new () = new Vec2D (0.0,0.0)        
    
    //暗黙的に生成されるメインコンストラクタとタプル内の
       //要素の個数が同じため必要
    [<OverloadID("1")>]
    new (xi :int ,yi :int) =
        let xf = float xi
        let yf = float yi
        //最後にメインコンストラクタを呼び出す
        new Vec2D (xf,yf)
 
実行例
 
> let t2 = new Vec2D (3,5);;
val t2 : Vec2D
 
> t2.ShowContents;;
x = 3.000000 y = 5.000000
val it : unit = ()
 
また、メソッドについては、同名のものを複数定義するときは常にオーバーロード属性の追加が必要となります。
 
それでは、次にmutableなインスタントフィールドをもつ、クラスを定義してみます。
F#の暗黙的クラス定義では、インスタントフィールドは直接的に定義するのではなくPrimary Constructors内で、let束縛されたものの内から自動設定されます。ということで、次のように定義してみます。
 
type Vec2D (xf : float,yf:float) =
    let mutable x = xf
    let mutable y = yf
     
    member this.ShowContents
                = printfn "x = %f y = %f" x y 
                  
    member this.X = x
    member this.Y = y
 
ちっとここで、ShowContentsを少し変更しておきます。これを今まではプロパティとして扱ってきましたが、意味合いとしては、メソッドのようがふさわしいと思われます。
よって、unit型を引数にするメソッドに変更します。ただ、次のようにすればよいだけです。
 
    member this.ShowContents ()
                = printfn "x = %f y = %f" x y 
 
 
さて、いままではimmutableな値を相手にしていましたから、値は読みだし専用(read-only)でしたが、mutableなフィールドが追加されると、再設定が可能になります。次のように、xの値を変化させれるようにしてみます。
 
type Vec2D (xf : float,yf:float) =
    let mutable x = xf
    let mutable y = yf
    
    member this.ShowContents ()
                = printfn "x = %f y = %f" x y 
                  
    member this.X = x
    member this.ChangeX nx =
        x <- nx
    
    member this.Y = y
 
実行例
 
> let t = new Vec2D (2.0,3.0);;
val t : Vec2D
 
> t.ShowContents ();;
x = 2.000000 y = 3.000000
val it : unit = ()
 
> t.ChangeX 10.0;;
val it : unit = ()
 
> t.ShowContents ();;
x = 10.000000 y = 3.000000
val it : unit = ()
 
どうでしょうか、ちゃんと変更できています。
ただ、このように値を変更するにはsetterというものを定義するのが普通のやり方なので通常は次のようにします。(下の例ではgetter(値をとって来る)も同時に定義しています。)
 
type Vec2D (xf : float,yf:float) =
    let mutable x = xf
    let mutable y = yf
    
    member this.ShowContents ()
                = printfn "x = %f y = %f" x y 
                  
    member this.X 
        with get() = x
        and set(nx) = x <- nx //getterだけならこの行を削除
                              //setterだけならwith set(nx) = x <- nxのみ
    
    member this.Y = y
 
使用例
 
> let t = new Vec2D (2.0,3.0);;
val t : Vec2D
 
> t.ShowContents ();;
x = 2.000000 y = 3.000000
 
> t.X <- 10.0;;
val it : unit = ()
 
> t.X;;
val it : float = 10.0
 
> t.ShowContents();;
x = 10.000000 y = 3.000000
val it : unit = ()
 
次に静的メソッド、静的プロパティ、静的フィールドのあるクラスを定義してみます。
 
type Vec2D (x : float,y:float) =
    
    //下のAddかVec2DAddを用いて何回足し算がクラス全体で行われたか
    static let mutable m_addCount = 0  
    
    member this.ShowContents
                = printfn "x = %f y = %f" x y 
    member this.X = x
    member this.Y = y
      
    member this.Add (t :Vec2D) =
            m_addCount <- m_addCount + 1
            new Vec2D(x + t.X,y + t.Y)
    
    static member Vec2DAdd (s:Vec2D,t:Vec2D) =
            m_addCount <- m_addCount + 1
            new Vec2D(s.X + t.X,s.Y + t.Y)          
    
    static member AddCountNum =
            m_addCount 
            
まず静的フィールドは、Primary Constructors内でstaticキーワードを付けて定義します。
(静的フィールドは必ずmutableです。)
staticが先頭(letより前)に来ることに注意してください。
インスタントメソッドであるAddと静的メソッドであるVec2DAddで、足し算が行われるたびに、m_addCountがインクリメントされるように設定します。m_addCountの値は静的プロパティであるAddCountNumで読みだすように設定します。静的メソッドもプロパティも先頭にstaticキーワードを付けて定義します。また,インスタンスに対して働くのではないので、this.が頭に付いていないことにも留意してください。
 
実行例
 
type Vec2D =
  class
    new : x:float * y:float -> Vec2D
    member Add : t:Vec2D -> Vec2D
    member ShowContents : unit -> unit
    member X : float
    member Y : float
    static member Vec2DAdd : s:Vec2D * t:Vec2D -> Vec2D
    static member AddCountNum : int
  end
 
> let t = new Vec2D(2.0,3.0);;
val t : Vec2D
 
> let s = new Vec2D(1.0,5.0);;
val s : Vec2D
 
> let u = t.Add (s);;
val u : Vec2D
 
> Vec2D.AddCountNum;;
val it : int = 1
 
> let v = Vec2D.Vec2DAdd(t,s);;
val v : Vec2D
 
> Vec2D.AddCountNum;;
val it : int = 2
 
では宿題です。上のクラスにインスタンスの個数を表示するプロパティNumOfInstancesを追加してください。
スポンサーサイト

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

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

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

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

この人とブロともになる

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