スポンサーサイト

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

F#入門中級編第9回(Computation Expression(9))

今回のお題は「Computation Expression(9) while」です。
 
まずは前回の宿題の解答から。
宿題
type ForBuilder02 () =
   let rArr = new ResizeArray<int> ()
 
   member this.Bind(x,rest) = 
     rArr.Add(x)
     rest x       
   member this.Return (x) = 
     (x,rArr)
   member this.Delay f = 
     f ()
   member this.Combine(a,b) =
     a 
     b 
   member this.Zero()  =
     ()
   member this.For (inp:seq<_>, f) =
    let rec loop(en:IEnumerator<_>)=
        if not(en.MoveNext()) then 
            this.Zero() 
        else
            f(en.Current)
            rArr.Add(en.Current)
            loop(en)
    loop(inp.GetEnumerator())
 
let forBld02 = new ForBuilder02()
 
let ForSample02 =
    let prt (x:int) = printfn "seq内の%Aを追加" x
    forBld02{
        let! x = 7
        for i in [|6;5|] do 
             prt i
        return (x) 
        }
        
 
解説
上は次のように解釈されます。
let ForSample022 =
    let prt (x:int) = printfn "seq内の%Aを追加" x
    forBld02.Delay(fun ()->
        forBld02.Bind(7,fun x ->
            forBld02.Combine(
                forBld02.For([|6;5|],prt),
                forBld02.Delay(fun () -> forBld02.Return(x))))) 
                
 
ということで実行すると
>
seq内の6を追加
seq内の5を追加
 
val ForSample02 : int * ResizeArray<int> = (7, <seq>)
 
この結果のタプルの第二成分を調べてみると
 
> let (_,t) = ForSample02;;
val t : ResizeArray<int>
 
> printfn "%A" t;;
seq [7; 6; 5]
val it : unit = ()
 
となります。
 
さて今回はmember.Whileを紹介します。
 
while文は
 
while expr do cexpr の形ですが、これは
 
b.While((fun () -> expr),b.Delay(fun () -> ≪cexpr≫))
 
の形に解釈されます。
ですから、通常の実装では、タプルの第一成分を()に作用させて評価して、真であれば第二成分を評価するという形になるかと思います。
 
それでは例を見てください。(これは実はうまくいかない例です。)
 
open System.Collections.Generic
type whileBuilder01 () =
   member this.Bind(x,rest) = 
      rest x      
   member this.Return (x) = 
      x
   member this.Delay f = 
     f ()
   member this.Combine(a,b) =
     a
     b
   member this.Zero()  =
     () 
    
   member this.While(gd,body)=
    let rec loop() =
        if gd () = false then //gd は fun() -> expr
            this.Zero()
        else
            body
            loop()
    loop()
 
let whileBld01 = new whileBuilder01()
 
let whileSample01 =
            whileBld01{
                let x = ref 0
                while !x < 3  do
                   printfn "x=%d" !x   
                   x := !x + 1
                
                } 
 
printfn "%A" whileSample01
 
これは実行するとx=0と表示されて、無限ループに陥ります。
 
これを解釈してみると次のようになります。
 
let WhileSample02= 
    whileBld01.Delay(fun () ->
        whileBld01.Bind(ref 0,fun x->
           whileBld01.Combine( 
                whileBld01.While((fun () -> !x < 3), 
                    whileBld01.Delay(fun () -> x := !x + 1;printfn "%A" !x)), //#1
                whileBld01.Delay(fun () -> whileBld01.Return(!x)))))
 
問題は#1とDelayの定義方法で、F#では定義するとすぐ評価にはいるので、Delayの定義より#1のfun () -> x := !x + 1;printfn "%A" !x部分まで評価されてしまい、X=0と表示されて、whileBld01.Delay(fun () -> x := !x + 1;printfn "%A" !x1の部分は()になってしまうのです。よってWhileの引数の第二成分として渡されるのは()になってしまい、xの値が変化することなく、無限ループの陥ります。
これを防ぐにはDelayの定義部分ですぐ評価せず、別のタイミングで評価するようにします。
例えば次のようにします。
 
open System.Collections.Generic
 
let delay func1 = fun () -> func1 ()
 
type whileBuilder01 () =
   member this.Bind(x,rest) = 
      rest x      
   member this.Return (x) = 
      x
   member this.Delay f = 
     delay f //すぐ評価しない、(()を渡すと評価を始めるようにします。)
   member this.Combine(a,b) =
     a
     b () 
   member this.Zero()  =
     ()
   member this.While (gd,body) =
    let rec loop() =
        if gd ()= false then 
            this.Zero ()
        else
            body () //()を渡して評価を始める
            loop ()
    loop()    
 
let whileBld01 = new whileBuilder01()
 
let whileSample01 =
            whileBld01{
                let x = ref 0
                while !x < 5  do
                   x := !x + 1
                   printfn "x=%d" !x
                return (!x)   
                } 
 
printfn "ans = %A" (whileSample01 ())
 
実行例
 

x=1
x=2
x=3
x=4
x=5
ans = 5
 
なおWhileの型は
 
(unit -> bool) * M<'a> -> M<'a>
 
となります。
スポンサーサイト

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

コメントの投稿

非公開コメント

CombineLoop

最初の失敗する方の、whileBuilder01 で定義されている CombineLoop は、なんですか?
初出だと思うんですが・

No title

すいませんCombineLoopは削除です。(訂正しました。)
プロフィール

T GYOUTEN

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

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

この人とブロともになる

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