スポンサーサイト

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

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

今回のお題は「Computation Expression(6)」です。
 
さてここまでで、memberのBind,Return,Delayについて見てきました。これら3つは必須で定義する必要があります。 (ただ、MSDNにおいてDelayは必須と書かれているのですが、今までの例では、定義していませんでした。定義しなくても、今のところは大丈夫みたいです。)
ここで、少し復習をしておきます。
まずは下の表を見てください。
 
    let! pat = expr in cexpr    b.Bind(expr,(fun pat -> ≪cexpr≫))
 
    let pat = expr in cexpr     b.Let(expr,(fun pat -> ≪cexpr≫))
 
    do! expr in cexpr           b.Bind(expr,(fun () -> ≪cexpr≫)) 
 
    do expr in cexpr            b.Let(expr,(fun () -> ≪cexpr≫))
 
    return expr                 b.Return(expr) 
 
 
patは識別子,bはbuilderの頭文字、exprは通常のF#のコードでの式(expression)
cexprはcomputation expression,<<cexpr>>は「cexprをbを使って翻訳したもの」を表します。
また、in cexprでの、cexprはその行以下のcomputation expression全体を表します。

なおLetについては、例えば
   member this.Let(x,rest) =  //#1
     rest x
と定義しても、computation expression内のlet,doの部分の解釈に自己定義のthis.Letは使われないという仕様になったので、#1のような感じで解釈されるんだなあと思っておいてください。
 
さてBind,Return,Delay以外にオプションで、定義できるものがあるので、これらをいくつか見ていきたいと思います。
 
    for pat in expr do cexpr    b.For(expr,(fun pat -> ≪cexpr≫))
 
    while expr do cexpr         b.While((fun () -> expr),b.Delay(fun () -> ≪cexpr≫))
 
    if expr then cexpr1 else cexpr2       if expr then ≪cexpr1≫ else ≪cexpr2≫ 
 
    if expr then cexpr      if expr then ≪cexpr≫ else b.Zero() 
 
これらはcomputation expression内のfor,while,ifなのですが、明らかに上のlet!等と違いがあります。
それがどこかというと、「in cexprがない」、すなわち後続部分とのつながり方が上では定義されていないのです。それでは、それはどうなるかというと、次のように定義されています。
 
    cexpr1
    cexpr2    b.Combine(≪cexpr1≫, b.Delay(fun () -> ≪cexpr2≫)) 
 
たとえば
for i in [1;2;3] do printfn "%d" i
return 0
なら
 
cexpr1がfor i in [1;2;3] do printfn "%d" i
cexpr2がreturn 0を表すことになります。
 
つまりCombineメンバーがfor,while,ifブロックと後続のcomputation expressionの橋渡しをする(計算を繋げる)役割を果たすことになります。
 
 
一つCombineを使った例を挙げてみます。
 
type CombineBuilder0 () =
   member this.Bind(x,rest) = 
      printfn "Bind"
      rest x      
   member this.Return (x) = 
     printfn "Return" 
     x
   member this.Delay f = 
     printfn "delay"    
     f ()
   member this.Combine(a,b) =
     printfn "Combine"    
     printfn "a : %A" a; a
     printfn "b : %A" b; b
     
 
let cbu0 = new CombineBuilder0()
 
let test000  =
        cbu0.Combine(printfn "left",
            cbu0.Return(2))
 
 
F# Interactiveに食わせると、次のようになります。
 
>  
left
Return
Combine
a : <null>
b : 2
 
もう一度
let test000  =
        cbu0.Combine(printfn "left",
            cbu0.Return(2))
     
に注釈をつけて評価を見てみます、
 
left    //Combineの引数の第1成分が評価される
Return  //Combineの引数の第2成分が評価される
Combine //上の二つの値に対してCombineが呼び出される
a : <null> //printfn "left"の評価結果unitすなわち<null>
b : 2      //cbu0.Return(2)の評価結果すなわち2
という形になります。
 
 
cexp1がunit型を返し、単に続いてcexp2を実行するのなら、Combine(a1,a2)の定義は
 
   member this.Delay f = 
     printfn "delay"    
     f ()
 
member this.Combine(a1,a2) =
    a1
    a2
 
とでもしておけば、
b.Combine(≪cexp1≫, b.Delay(fun () -> ≪cexp2≫)) 
と評価されるのですから、「cexp1の評価をしてから、cexp2の評価を始める」という通常の処理をしてくれます。
 
なおCombineの型は
M<'a> * M<'a> -> M<'a>  
もしくは
M<unit> * M<'a> -> M<'a> 
となります。

 

スポンサーサイト

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

コメントの投稿

非公開コメント

No title

>これら3つは必須で定義する必要があります
例えば、最初の回、2回目の宿題の回答で、Delay は、定義されていないのに、
ここで必須だと説明するのはおかしくないですか?

以前第1回で、this.Let ができなくなったと書いているのに、
this.Let について説明するのはなぜですか?
バージョン1.9.6.16 でも、(this.Letを定義しても呼び出されない)機能しなくなっています。

b : 7 → 2

No title

BLUEPIXYさん、コメントありがというございます。
ここで必須だと説明するのはおかしくないですか->そうですよね。でも、MSDNには必須と書かれている。ということで、少し追記をくわえておきました。
this.Let について説明するのはなぜですか->ここも書き方が悪いですね。どのように、解釈されるかは知っておく必要があるけど、自分では定義できない。ということで、すこし書き変えました。
b : 7 → 2  ->すいません訂正しました。

No title

>MSDNには必須と書かれている
そうですね。
それは、読んでいたので知っていました。
なので、逆に、この連載で、必須じゃないじゃん(なくてもOK)なので、びっくりした記憶があります。

>どのように、解釈されるかは知っておく必要がある
あとの部分が、結局関数適用として呼び出される(Delayで?)というのは、わかりますけど、
それを sb0.Letの呼び出しになると説明するのは、実際には呼び出されないのだから
説明がおかしいと思います。

No title

それを sb0.Letの呼び出しになると説明するのは、実際には呼び出されないのだから
説明がおかしい -> 確かにおっしゃる通りなので、該当部分を削除しました。

No title

その・私がいちゃもんつけるからって、別に迎合する必要はないですよ。
(削除しなくても良かったんじゃないかと・)
私の言いたかったことは、
this.Let は、呼び出されなくなったのだから、sb0.Letが呼び出されるのはおかしい。
ということで、
そこの部分は、結局システムデフォルトのLet 呼び出しつまり最初の一覧表にあるようにおそらく匿名関数(ラムダ:fun ->)でくくられるんだから、
単にfun にすれば良かったんじゃないかと思いますけど。

No title

色々説明を加えていると冗長になってきて、どんどん本線から外れてきたので、バッサリ切って見直してみると、こちらの方が流れとして良いと思って削除しました。
「this.Letは定義できるけど、computation expression内のletの解釈としては使われない」という仕様は、なんとかしてもらいたいものです。
仕様も固まっていない感じだし、ドキュメントも不足。(β版だから、あたりまえ?)

No title

>letの解釈としては使われない
私は、
let! , do! があるから、特別必要なくて
そういうビルダーで解釈されない部分をワークとして実行できるのは、いいんじゃないかと思います。
プロフィール

T GYOUTEN

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

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

この人とブロともになる

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