スポンサーサイト

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

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

今回のお題は「Computation Expression(11) the state workflow(1)」です。
 
stateとは、直訳すると「状態」です。まずは次のコード片をみてください。
let test =
    state 
      { let! x = GetState
        printfn "init x = %d " x 
        let y = 100
        do! SetState (x + y * 100)
        return (x + 50)
       }
 
ある種のstateBuilderを作成し、それのインスタンスをstateとしたとき実はtestは一つのint -> (int*int)型の関数のオブジェクトになります(詳しくは後で説明)。
いくつかの値に対して実行すると次のようになります。
 
//Runは関数のオブジェクトを関数として適用する自前の関数(詳しくは後で説明)
> Run test 1;; 
init x = 1 
val it : int * int = (51, 10001)
 
> Run test 2;;
init x = 2 
val it : int * int = (52, 10002)
 
GetState,SetStateの説明もしていないので、分かるわけはないのですが、とりあえずは、返り値のタプルの第一成分は(x+50)の値が、第二成分はSetStateしたときの(x+y*100)の値になっています。
とにかく、表に出ている普通の計算と同時にバックグラウンドで「何らかの状態(state)」の計算がなされている、すなわち2重計算されている、という感じをもってください。
 
それでは、このような2重計算を可能にするために、一つのクラスを導入します。
 
type StateFunc<'state,'a> = StateFunc of ('state -> 'a * 'state)
 
これは一つの種類しか持たないDiscriminated unionですが、Discriminated unionは内部的にクラス化されますので、newを使わずにインスタンスを生成できる手軽なクラスとして使用できます。たとえば次のようにオブジェクトを作成します。
 
> let sf = StateFunc(fun s -> (s + 1,s + 2));;
val sf : StateFunc<int,int> = StateFunc <fun:k@5>
 
内部の関数を値に適用するようにRunという関数を次のように定義しておきます。
 
//StateFunc内部の関数をsに作用させる
>let Run (StateFunc f) s = f s;;
val Run : StateFunc<'a,'b> -> 'a -> 'b * 'a
 
使ってみます。
> Run sf 5;;
val it : int * int = (6, 7)
 
さてStateFuncは'state -> 'a * 'state型の関数を内部にもつオブジェクトでしたが、この形の意味は、「「状態」を受け取って、「表向きの値」と「新しい状態」を返す」という意味合いです。
 
 
さてそれでは、解釈ルールを述べていきます。
 
type StateBuilder () =
    //このBindの定義がState workflowのキモ(説明は後で)
    member this.Bind(stfunc,rest) = 
        StateFunc(fun old_s ->
                let v,new_s = Run stfunc old_s
                Run (rest v) new_s
             )                         
    member this.Return (x) = 
        StateFunc(fun s -> (x,s))
 
let state = StateBuilder()
 
それでは次のコードを変換してみましょう。
let example01 : StateFunc<int,int> =
      state
        { return 20}   
 
さてこれは
 
let example01 =
    state.Return 20
 
と解釈されるので
 
let example01 =
     StateFunc(fun s -> (20,s))
 
つまり状態は変化させずに、値は20を返すというStateFunc型のオブジェクトを返すことになります。表向きの値の処理としては普通のreturnの役割をして、状態は変化させないということです。
 
次はこれです。
let example02 : StateFunc<int,int> =
      state
        {     let x = 19
            return x}  
 
変換してみます
 
let example02 =
    state.Let(19,fun x ->
                state.Return x)
                
よって
let example02 =
    state.Let(19,fun x ->
                StateFunc(fun s -> (x,s)))
                
State.Let(x,rest) = rest x と解釈すればよいので
let example02 =
    StateFunc(fun s -> (19,s))
 
となり、「状態は変えずに、値としては19をかえす」というStateFunc型のオブジェクトを返すことになります。
 
つぎにbind部分を考察します。定義を再掲します。
 
    member this.Bind(stfunc,rest) = 
        StateFunc(fun old_s ->
                let v,new_s = Run stfunc old_s //**1
                Run (rest v) new_s             //**2
             ) 
 
これも不思議な定義で、返されるものが、StateFuncオブジェクトです。
引数のタプルの第1成分のstfuncはlet!式の右辺で
引数のタプルの第2成分のrestというのは、let!以下の計算をマトリョーシカ化して、let!の左辺の値の型の鍵穴を付けたものです。
よってこのメソッドは「old_s(旧状態)が渡されたら、それにlet!式の右辺の表すstfuncを作用させて、値vと新しい状態new_sを手に入れます。ここでrest v というのは、残りの計算内の左辺の値をvに置き換えた残りの計算全体であるStateFuncオブジェクトですから、これをnew_sに作用させる」StateFuncオブジェクトを返します。
 
まだまだ分かりにくいので、次を翻訳してみましょう。
state{
    let x=3      //0
    let! u = sf1 //1
    let y = 4    //2
    let! v = sf2 //3
    ......... 
    return k}
は,1行目以降のxを3で置き換えたものとして
state{
    let! u= sf1
    let  y = 4
    let! = sf3
    .........
    retrun k}
の形になります。
これは
State.Bind( sf1,fun u -> //&1&
        State.Let (4,fun y -> //&2&
            State.Bind(sf2,fun v -> //&3&
                .....................
                    ->State.Rerturn k ))...))
 
ここで、StateBindの定義から、これ全体がひとつの、StateFuncオブジェクトとなります。
また、マトリョーシカで&1& の部分ですることは、もしstateがやってきたら、それにsf1を作用させて、値uの新しい値new_vと新しいstateを手に入れます。そのnew_vで鍵を開いて(それより内部のuを、new_vで置き換えること)、計算を続けます。&2&では、ただ単に4で鍵を開きます。(それより内部のyを4で置き換えること)するとまたlet!に対応する&3&以下の計算の表すStateFuncマトリョーシカが出てきますから、同様の&1&でしたのと同様の操作を新しいstateに対して行うわけです。
よって、最初のstateにこの全体のStateFuncオブジェクトを作用させると、どんどんマトリョーシカが開いていきますが、stateの状態に変化を及ぼすのは&1&と&3&の部分で、この行は同時に表の値の変化にも寄与します。
 
大雑把にまとめます。StateFuncマトリョーシカというのは、それ自体がStateFuncオブジェクトです。その内部構造がどのようになっているかというと、let!に対応する部分では、let!の左辺の値の型の鍵穴があるマトリョーシカの手前にStateFuncオブジェクトsfが吊ってあります。(あくまで比喩です。)状態がやってきたら、吊ってあるsfを作用させて、値と新しい状態new_sを手にいれて、まず値を鍵に押し込みマトリョーシカを開き、次にStateFuncオブジェクトsfNextが吊ってあるマトリョーシカにいきついた時点でnew_sに対してsfNextを作用させて.....というようなことをして開いていきます。
それ自体としてはStateFuncオブジェクトで、状態に作用させると、(値、状態)の形のタプルが返ります。
 
まだ、分かりにくいかもしれないので、いくつか練習をしてみます。
 
さて次のように定義してRun Drill01 5 とすると返り値はなんでしょうか?
 
let Drill01 = 
    state{                                      //#0
        let! x = StateFunc (fun s -> (s,2*s))   //#1
        let  y = 100 * x                           //#2
        let! z = StateFunc (fun s -> (7*s,3*s)) //#3
        return (x + y + z)                         //#4
    }
 
答えは(575,30)です。
解説します。まず5が状態としてやってくるので、#1部分のマトリョーシカ(鍵穴x)の部分で、5に右辺の表すStateFuncオブジェクトを適用します。すると(5,10)となります。第一成分が値xとして、マトリョーシカに押し込みます。よってこれ以降のxは5に置き換えられます。状態は10です。
次に#2の部分では、yが100*5で500となります。#3部分のマトリョーシカ(鍵穴x)の部分で、現在の状態である10に右辺の表すStateFuncオブジェクトを適用します。すると(70,30)となります。。第一成分の70が値zとして、マトリョーシカに押し込まれます。よってこれ以降のzは70に置き換えられます。状態は30です。
最後に#4で、x+y+zは5+500+70で575、状態が30ですので、これのタプル(575,30)が返ります。
 
値の変化を表すと次のようになります。
  (  x  ,    y   ,    z )   状態
#0 未定義 未定義   未定義     5
#1    5   未定義   未定義    10     (fun s -> (s,2*s))
#2    5      500   未定義    10
#3    5      500       70    30     (fun s -> (7*s,3*s)
 
それでは宿題です。
(宿題)
次のように定義してRun Drill02 (3,[]) とすると返り値はなんでしょうか?
 
let Drill02 = 
    state{
        let! x = StateFunc (fun (sn,sl) -> (5*sn,(2*sn,(2*sn):: sl )))  //#1
        let  y = 100 * x                                                //#2
        let! z = StateFunc (fun (sn,sl) -> (7*sn,(3*sn,(3*sn):: sl )))  //#3
        return (x + y + z)                                              //#4
    }

スポンサーサイト

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

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

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

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

この人とブロともになる

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