スポンサーサイト

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

F#入門中級編第18回(Asynchronous Workflows(4))

今回のお題は「Asynchronous Workflows(4) 」です。
 
非同期処理が良く使われる場面にインターネットからのページ読み込みがあり、これについては、それ用のメソッドまで準備されているので、まず今回はこの例を取り上げたいと思います。まずはコードを示します。
 
open System.IO
open System.Net
open Microsoft.FSharp.Control.WebExtensions // AsyncGetResponseを使う為
 
 
let getHtml (url : string) =
    async {
        let req = WebRequest.Create(url)
        let! rsp =  req.AsyncGetResponse()
        let stream = rsp.GetResponseStream()
        use reader = new StreamReader(stream)
        return reader.ReadToEnd()
}
 
実行結果
 
val getHtml : string -> Async<string> //これは非同期処理として実行可能で結果としてstringが返るという意味
 
それでは
 
    async {
        let req = WebRequest.Create(url)
        let! rsp = req.AsyncGetResponse()
        ..................................
 
の部分を疑似的に書き直してみます。
 
async.Delay(fun () ->
    async.Let(WebRequest.Create(url),fun req ->
        async.Bind(req.AsyncGetResponse(),fun rsp ->
            ..............................
 
まず最初のDelayですが、これがあることにより、定義してすぐ評価(実行)されることを防ぎます。
次の行で、リクエストの準備を整えます。ここでasync.Bindの登場ですが、まずこれの型を調べてみます。
 
> async.Bind;;
val it : (Async<'a> * ('a -> Async<'b>) -> Async<'b>) = <fun:it@3>
 
ということでreq.AsyncGetResponse()の型はAsync<'a>で、「ページを読み込んで、リクエスト内容を返す」という非同期処理を表します、('a -> Async<'b>)が継続形式で与えられるその後の処理になります。
実際にはAsyncGetResponseはunit->Async<WebResponse>型です。つまり非同期処理として実行可能で、その結果WebResponse型の値を返すメソッドということです。(上で'aがWebResponseの場合となります。)
async workflowsは内部的には、スレッドプールとtrampolineを利用して実装されているみたいです。
(trampolineについてはhttp://www.infoq.com/jp/articles/js_multithread_2が参考になるかと思います。)
 
このようにAsynchronous Workflowsを利用することで、時系列の混乱なく非同期処理を書くことができます。
 
なお
let html =
    getHtml "http://ajex2.web.fc2.com/index.htm"
    |> Async.RunSynchronously
 
として実行してみると次のようになります。
 
val html : string =
  "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"+[1358 chars]
 
 
また上のgetHtml関数を使って複数のページを文字列としてリストに読み込むには次のようにします。
 
[siteUrl1;siteUrl2;siteUrl3]
|>List.map getHtml
|>Async.Parallel
|>Async.RunSynchoronously
 
次にmember this.Returnを紹介します。これの型は
 
> async.Return;;
val it : ('a -> Async<'a>) = <fun:it@5>
となっており、値を、「実行するとその値を返す非同期処理」として返します。
使ってみます。
 
> let aSum (x,y) = 
    async{
        return (x + y) };;
 
val aSum : int * int -> Async<int>
 
> Async.RunSynchronously ((aSum (2,4)));;
val it : int = 6
 
> [(2,4);(4,9);(1,2)]
|>List.map aSum
|>Async.Parallel
|>Async.RunSynchronously;;
val it : int array = [|6; 13; 3|]
 
なおaSum (a,b) 自体の型もAsync<_>型なので、async{..}内で、let!の右辺として使用可能です。

> let test =
    async{
        let! t1 = aSum (2,4)
        let! t2 = aSum (4,9)
        let! t3 = aSum (1,2)
        return [t1;t2;t3]
        };;
 
val test : Async<int list>
 
> Async.RunSynchronously test;;
val it : int list = [6; 13; 3]
 
またAsynchronous Workflows内でreturn! u (uはAsync<_>型)は、uを非同期実行してその結果を返します。
 

 
> let test2 =
    async{
        return! test
        };;
 
val test2 : Async<int list>
 
> Async.RunSynchronously test2;;
 
val it : int list = [6; 13; 3]
 
以前例で取り扱ったwhatToDo関数もAsynchronous Workflowsで取り扱うと次のように簡単になります。
 
> let whatToDo (s:string,n:int) = 
    async{
            for i in 0 .. (n - 1) do
                printfn "%s" s
            return (10*n) };;
 
val whatToDo : string * int -> Async<int>
 
> Async.RunSynchronously (whatToDo ("a",12)) ;;
a
a
a
中略
a
val it : int = 120
 
> let test =
    async{
        let! t1 = whatToDo ("a",25)
        let! t2 = whatToDo ("b",9)
        return [t1;t2]
        };;
 
val test : Async<int list>
 
> Async.RunSynchronously test ;;
a
a
中略
b
b
b
val it : int list = [250; 90]
スポンサーサイト

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

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

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

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

この人とブロともになる

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