スポンサーサイト

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

F#入門第39回(OOP(4)明示的クラス定義)

今回のお題は「OOP(4)明示的クラス定義」です。
 
まずは宿題の解答例からです。
(宿題)
。次の二つのクラスがあります。
 
type BaseClass (x : int,y:int) =
 
    abstract member ShowContents :unit -> unit
           default this.ShowContents ()
                    = printf "x = %d y = %d " x y 
    member this.X = x
    member this.Y = y
 
type DerivedClass (x , y ,z :int) =
    inherit BaseClass(x,y)
    
    override this.ShowContents ()
                =   base.ShowContents()
                    printf "z = %d " z 
     
    member this.Z = z
 
さらに、BaseClassにzを追加してDerivedClassを派生させたように、DerivedClassにuを追加してDerived2Classを派生させてください。、
 
type Derived2Class (x,y,z,u:int) =
    inherit DerivedClass (x,y,z)
    
    override this.ShowContents ()
                = base.ShowContents ()
                  printf "u = %d" u
                 
    member this.U = u
    
実行例
 
> let t = Derived2Class (1,2,3,4);;
val t : Derived2Class
 
> t.ShowContents();;
x = 1 y = 2 z = 3 u = 4 val it : unit = ()
 
さて、今回の話題にはいります。
暗黙的にクラス定義するには、
type クラス名 仮引数(=初期化に使用する値のタプル) =
    Primary Constructors
    プロパティ、メソッドの定義
の形を用いるのでした。
するとF#が、仮引数とPrimary Constructorsとを、分析して、クラスフィールドとデフォルトのコンストラクタ(引数は上の仮引数と同じでメインコンストラクタと呼ばれる)を自動的に暗黙的に定義してくれるのでした。
明示的クラス定義では、この部分を明確に自分で定義します。
すなわち、クラスフィールドとコンストラクタ(これは、どれがメインということもなく、すべて同等です。)を自分で定義する必要があります。(暗黙的クラス定義と異なり、自動的にコンストラクタが、生成されることはないので、インスタンスを生成したいのなら、
必ず一つは自分で定義する必要があります。)
例えば次のようになります。
 
 type BaseClass  =
    val _x : int
    val _y : int
    
    new (x,y) = {_x = x ; _y = y}
    new ()    = {_x = 0 ; _y = 0}
 
 まずclass定義の最初に、フィールドをvalキーワードを用いて定義します。
 つづけて、コンストラクタですが、定義部分は{}で囲み、;で区切ってフィールド名 = 初期値 を並べて書きます。 
  
実行例
type BaseClass =
  class
    new : unit -> BaseClass
    new : x:int * y:int -> BaseClass
    val _x: int
    val _y: int
  end
 
> let t = new BaseClass (1,2);;
val t : BaseClass
 
> t;;
val it : BaseClass = FSI_0006+BaseClass {_x = 1;
                                         _y = 2;}
> let s = new BaseClass ();;
val s : BaseClass
 
> s;;
val it : BaseClass = FSI_0006+BaseClass {_x = 0;
                                         _y = 0;}
 
また、{}の前に、let束縛及びunit型の式を付け加えることができます。let束縛された値は{}の中で使用可能です。
 

type BaseClass  =
    val _x : int
    val _y : int
    
    new (x,y) = 
        //前処理
        let k = x * x
        let v = if y > 0 then y
                else (-1)* y
        printfn "now init _x to %d _y to %d" k v
        {_x = k ; _y = v}
     
    member this.ShowContents() =
        printf "x = %d y = %d " this._x this._y 
        
実行例
 
> let t = new BaseClass (2,-1);;
now init _x to 4 _y to -1
 
> t.ShowContents();;
x = 4 y = -1 val it : unit = ()
 
また、{}の後にthenにつづけて、unit型の式を付け加えることができます。
次の例ではnewのあとにas演算子も使ってみています。
 
type BaseClass  =
    val _x : int
    val _y : int
    
    new (x,y) as this = //生成されるインスタンスがthis
        //前処理
        let k = x * x
        let v = if y > 0 then y
                else (-1)* y
        printfn "now init _x to %d _y to %d" k v
        {_x = k ; _y = v}
        //後処理
        then
            printfn "now inited _x is %d _y is %d"  this._x this._y
     
    member this.ShowContents() =
        printf "x = %d y = %d " this._x this._y 
 
実行例
 
> let t = new BaseClass (2,-1);;
now init _x to 4 _y to 1
now inited _x is 4 _y is 1
 
続いてメソッド,プロパティを追加してみます。
 
type BaseClass  =
    val _x : int
    val _y : int
    
    new (x,y) = {_x = x ; _y = y}
    new ()    = {_x = 0 ; _y = 0}
    
    member this.ShowContents() =
        printf "x = %d y = %d " this._x this._y 
        
    member this.X = this._x
    member this.Y = this._y
 
 
 暗黙的クラス定義と似ていますが、少し違います。
 インスタントメソッド,プロパティを定義の際、 暗黙的クラス定義では初期化に使用する値のタプル内の値と、Primary Constractors内でlet束縛された識別子にアクセス可能だったのですが、明示的クラス定義で、インスタントフィールドにアクセスするには、this.フィールド名という形でアクセスしなくてななりません。 (実際には、thisでなく、thisの部分を全部vやら、tやらに変更してもエラーはでませんが、thisまたはselfを使うことをお勧めします。)
 
次 はメソッドのオーバーロードですが、これは暗黙的クラス定義の場合と同様です。

 
type BaseClass  =
    val _x : int
    val _y : int
    
    new (x,y) = {_x = x ; _y = y}
    new ()    = {_x = 0 ; _y = 0}
    
    [<OverloadID("111")>]
    member this.ShowContents() =
        printf "x = %d y = %d " this._x this._y 
    
    [<OverloadID("222")>]
    member this.ShowContents(s) =
        printf "x = %d y = %d %s" this._x this._y s
 
実行例
 
> let t = new BaseClass (1,2);;
val t : BaseClass
 
> t.ShowContents();;
x = 1 y = 2 val it : unit = ()
 
> t.ShowContents("だよ");;
x = 1 y = 2 だよval it : unit = ()
 
またgetterとsetterですが、インスタントフィールドにアクセスするにはthis.フィールド名という形でアクセスすることだけに留意しておけば、暗黙的クラス定義と同じです。
 
次に静的メソッド、静的プロパティ、静的フィールドのあるクラスを定義してみます。
まず静的フィールドですが、静的フィールドは必ずmutableに設定する必要があります。。明示的クラス定義では、さらに、初期値を定義部分で設定できないので、[<DefaultValue>]属性も付け加える必要があります。(これによって、ゼロかnullに初期化されます。)

    [<DefaultValue>]
    static val mutable _instanceNum : int
 
プロパティ、メソッドの定義では上の識別子にアクセスするのに、クラス名.静的フィールド名の形でアクセスする必要があります。

        static member InstanceNum = BaseClass._instanceNum
 
ということで、クラス定義全体の例をひとつ
 type BaseClass  =
    val _x : int
    val _y : int
    [<DefaultValue>]
    static val mutable _instanceNum : int
    
    new (x,y)  = 
        BaseClass._instanceNum <- BaseClass._instanceNum + 1
        {_x = x ; _y = y}
     
    member this.ShowContents() =
        printf "x = %d y = %d " this._x this._y 
    
    static member InstanceNum = BaseClass._instanceNum
 
実行例
 
type BaseClass =
  class
    new : x:int * y:int -> BaseClass
    val _x: int
    val _y: int
    member ShowContents : unit -> unit
    static val mutable _instanceNum: int
    static member InstanceNum : int
  end
  
> BaseClass.InstanceNum;;
val it : int = 0
 
> let t = new BaseClass(1,2);;
val t : BaseClass
 
> BaseClass.InstanceNum;;
val it : int = 1
 
次にアクセス制御ですが、例えば次の例で###?###の部分が挿入可能部分です。
 
type ###1### BaseClass  =
    val ###2### _x : int
    val ###2### _y : int
    [<DefaultValue>]
    static val mutable ###2### _instanceNum : int
    
    ###3### new (x,y)  = 
        {_x = x ; _y = y}
     
    member ###4### this.ShowContents() =
        printf "x = %d y = %d " this._x this._y 
   
1がクラスに対する、2がフィールドに対する、3がコンストラクタに対する、4がメソッド、プロパティに対するものです。
 
最後に継承です。
 

 
 type BaseClass  =
    val _x : int
    val _y : int
    
    new (x,y) = {_x = x;_y = y}
    new ()    = {_x = 0;_y = 0}
    
    member  this.ShowContents ()
                    = printf "x = %d y = %d " this._x this._y 
 
上のクラスから、新しいクラスを派生してみます。
まず、定義の最初に派生元のクラスをinheritに続けて書きます。
次に、新しいフィールドがあれば並べます。
留意してほしいのは、コンストラクタで、{}の中の最初で、inheritに続けて派生元のコンストラクタのどれかを指定して、その後、追加分のフィールドの初期化を行います。
 
type DerviedClass =
    inherit BaseClass
    
    val _z : int
    new(x,y,z) = {inherit BaseClass(x,y) ;_z = z}
    new()      = {inherit BaseClass() ; _z = 0}
 
それでは宿題です。
次の暗黙的クラス定義を明示的クラス定義に書き直してください。
 
type Vec2D (x : float,y:float) =
    
    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) =
            addCount <- addCount + 1
            new Vec2D(x + t.X,y + t.Y)
    
    static member Vec2DAdd (s:Vec2D,t:Vec2D) =
            addCount <- addCount + 1
            new Vec2D(s.X + t.X,s.Y + t.Y)          
    
    static member AddCountNum =
            m_addCount 
 
スポンサーサイト

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

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

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

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

この人とブロともになる

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