スポンサーサイト

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

F#入門第42回(OOP(7)インターフェイス(1))

今回のお題は「OOP(7)インターフェイス(1)」です。
 
まずは宿題の解答からです。
(宿題)
キューとは、先に入力したデータが先に出力されるという特徴をもつ、データの容器です。。ちょうど遊園地の乗り物待ちのような構造になっており、データを入れるときは新しいデータが最後尾につき、データを出すときは一番古いデータが優先して出てくるという特徴をもちます。
上のMyIntStackクラスに手を加えて、MIntQueueクラスを定義してください。
データの追加はEnqueue(k:int)、取り出しはDequeue()で行うものとします。
練習なので、効率は悪いですがList.appendか@演算子を使用してください。
(解答例)
type MyIntQueue () =
    let mutable lst :int list = []
    member this.Enqueue (k : int) =
                lst <- List.append lst [k]
    member this.Dequeue () =
              match lst with
              | hd ::tl -> lst <- tl
                           Some(hd)
              | []      -> None
(実行例)
> let t = MyIntQueue();;
val t : MyIntQueue
 
> t.Enqueue(7);;
val it : unit = ()
 
> t.Enqueue(3);;
val it : unit = ()
 
> t.Dequeue();;
val it : int option = Some 7
 
> t.Dequeue();;
val it : int option = Some 3
 
> t.Dequeue();;
val it : int option = None

 
それでは、今回のお話に入ります。
インターフェイスというのは、クラスに対する資格条件のようなものです。もっと言うと、「これこれのプロパティとメソッドを実装していれば、このインターフェイスを取得していると認定する」と書いてある、認定条件書です。
それでは、認定条件書であるインターフェイスの定義の方法から始めます。
これは簡単で、すべてabstractで(defaultの実装なし!)プロパティとメソッドのみ(フィールド、コンストラクタなし!)を定義した型を書けば、自動的にインターフェイスの定義となります。

type ImyNumable =
    abstract Num: int
    abstract ShowNum : unit -> unit
 
実行例
 
type ImyNumable =
  interface
    abstract member Num :  int
    abstract member ShowNum : unit -> unit
  end
 
種推論機構に頼らずに明示的にインターフェイスだと定義するには上のように、interface endで囲みます。
さてこの認定条件書の内容を見てみましょう。これには、int型を返すNumという名のプロパティと、unit型すなわち()を受け取ってunit型を返すShowNumというメソッドを定義することと書いてあります。では、この認定をクリアするクラスを定義してみましょう。
前回出てきたMyIntStack型が、このImyNumable認定をクリアするように修正を加えます。
 
(修正前)
type MyIntStack () =
    let mutable lst :int list = []
    member this.Push (k:int) =
                lst <- k ::lst
    member this.Pop () =
              match lst with
              | hd ::tl -> lst <- tl
                           Some(hd)
              | []      -> None
 
(修正後)
type MyIntStack () =
    let mutable lst :int list = []
    member this.Push (k:int) =
                lst <- k ::lst
    member this.Pop () =
              match lst with
              | hd ::tl -> lst <- tl
                           Some(hd)
              | []      -> None
   //ここから後が追加分
    interface ImyNumable with
        member this.Num =
                 List.length lst
        member this.ShowNum () =
                 printfn "要素数は%dです" (List.length lst)    
 
上のようにinterface インターフェイス名 withと書き、その下に、認定条件書(インターフェイスの定義)に書かれた、プロパティ、メソッドを実装していきます。
F#Interactiveで実行してみると、以下のようになります。
type MyIntStack =
  class
    interface ImyNumable
    new : unit -> MyIntStack
    member Pop : unit -> int option
    member Push : k:int -> unit
  end
ちゃんとImyNumableとして認定していますと「interface ImyNumable」の表示が増えています。
さて、これらのメソッド、プロパティを使ってみます。
 
> let t = new MyIntStack();;
val t : MyIntStack
 
> t.Push (4);;
val it : unit = ()
 
> t.Num;;
 
t.Num;;
  --^^^
C:\Users\T_GYOUTEN\AppData\Local\Temp\stdin(6,3): error FS0039: The field, constructor or member 'Num' is not defined. 
 
> t.ShowNum();;
 
  t.ShowNum();;
  --^^^^^^^
 
C:\Users\T_GYOUTEN\AppData\Local\Temp\stdin(7,3): error FS0039: The field, constructor or member 'ShowNum' is not defined.
 
と、せっかく定義したのに使えないという、手ひどい裏切りにあいます。
これは、F#では、あるインターフェイスで要求され実装した、メソッド、プロパティを呼び出すには、そのインターフェイス型にキャストする必要があるのです。
よって、次のように呼び出します。
 
> (t :> ImyNumable).ShowNum();;
要素数は1です
val it : unit = ()
 
では、同様に、宿題で作ったMyIntQueueクラスも、ImyNumableインターフェイスを実装してみます。
 
type MyIntQueue () =
    let mutable lst :int list = []
    member this.Enqueue (k : int) =
                lst <- List.append lst [k]
    member this.Dequeue () =
              match lst with
              | hd ::tl -> lst <- tl
                           Some(hd)
              | []      -> None
    interface ImyNumable with
        member this.Num =
                 List.length lst
        member this.ShowNum() =
                    printfn "要素数は%dです" (List.length lst)
 
こうすると、MyIntStackクラスもMyIntQueueクラスも、ともにImyNumable認定を受けたことになりますから、キャストすることによって、どちらもImyNumable型として、同じ扱いをすることができます。
 
例、ImyNumable型の値を引数にする関数を作る。
let DispNum (x : ImyNumable) =
    match x.Num with
    | y when y > 0 -> printfn "要素数は%dです" y
    | _            -> printfn "空っぽです"    
    
実行例(関数の引数でインターフェイス型を指定している場合はキャストの必要なく呼び出せます)
 
let t1 = MyIntStack()
t1.Push(3)
t1.Push(4)
> DispNum (t1);;
要素数は2です
val it : unit = ()
 
> DispNum (new MyIntQueue());;
空っぽです
val it : unit = ()
 
例、一つのリストに入れる。
 
let t1 = MyIntStack()
t1.Push(3)
t1.Push(4)
let t2 = MyIntStack()
t2.Push(9)
let u1 = MyIntQueue()
u1.Enqueue(10)
let u2 = MyIntQueue()
u2.Enqueue(49)
u2.Enqueue(42)
としておいて、
let lst = [(t1 :> ImyNumable);(t2 :> ImyNumable);(u1 :> ImyNumable);(u2 :> ImyNumable)]とすると、もともとは、継承関係にない型のインスタンスが、ImyNumable型のリストの要素として一つのリストに入れることができます。
例えば、
> let nLst = List.map (fun (x :ImyNumable) -> x.Num) lst ;;
で、それぞれのスタック、キューの要素数のリストが出来上がります
実行例
val nLst : int list = [2; 1; 1; 2]
 
ということで、ある資格条件を満たしているものに対しての処理を(クラスの継承関係とは無関係に)書くことができるのです。これが、一番のインターフェイスの強みです。
 
また、クラスと同様にgenericなインターフェイス型もあり、例えば次のようなインターフェイスを定義できます。
//特に何の意味もない例用のインターフェイスです。
type ImyExampleable <'a> =
    abstract member Something : 'a
    
例えば実装側では次のように実装します。
type MyIntStack () =
    let mutable lst :int list = []
    member this.Push (k : int) =
                lst <- k ::lst
    member this.Pop () =
              match lst with
              | hd ::tl -> lst <- tl
                           Some(hd)
              | []      -> None
    interface ImyNumable with
        member this.Num =
                 List.length lst
        member this.ShowNum() =
                    printfn "要素数は%dです" (List.length lst) 
    interface ImyExampleable <int> with
        member this.Something = 7 //単に7を返すだけ
 
実行例
type MyIntStack =
  class
    interface ImyExampleable<int>
    interface ImyNumable
    new : unit -> MyIntStack
    member Pop : unit -> int option
    member Push : k:int -> unit
  end
スポンサーサイト

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

コメントの投稿

非公開コメント

typo

簡単ですべて、すべて → 簡単で、すべて ?

type ImyNumable =
abstract Num:unit -> int ← unit -> int ではなく、 int のはず
abstract ShowNum : unit -> unit

Re: typo

訂正しました、ありがとうございました。BLUEPIXYさんブログでBrainf*ckを実装されてましたね。私は「Rubyで作る奇妙なプログラミング言語」を購入したのですが、全然読めていません。

「Rubyで作る奇妙なプログラミング言語」

私も、ずっと買って置いておくだけだったのです(興味のある本は、とりあえず買っておかないと出回らなくなる可能性が・)が、最近読みました。
“Rubyで作る”なのに、Rubyを使わないところが…

ところで、ここのコメントもソースのスペースが・・
プロフィール

T GYOUTEN

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

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

この人とブロともになる

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