スポンサーサイト

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

F#入門初級編第7回(Lazy Computations)

今回のお題は「Lazy Computations 」です。
 
Lazyというのは、「怠惰」という意味で、ここでは「「やりなさい」と言われた時しか、やらない」という意味でとらえておいてください。一般的に「遅延評価」と訳されますが、遅らせるだけでなく、やらない場合もあるので注意が必要です。
 
例えば
> let x = 3
let result = x + 7;;
とすると
 
val x : int = 3
val result : int = 10
 
というように、普通やりなさいと言わなくても、F#はx + 7の値を評価(計算)します。
これを怠惰モード(「やりなさい」と言われた時しか、やらない)に移行させるのがLazy Computationsです。
 
怠惰モードに移行するには、「評価される式」をlazy( と )で囲みます。
先ほどの例では次のようになります。
 
> let x = 3
let result = lazy( x + 7);;
 
val x : int = 3
val result : Lazy<int> = Value is not created.
 
ここでresultの型に留意してください。
Lazy<int>となっています。これは、「やれと言われたら働いてintを返しますよ」という意味になります。
ではやってもらいましょう。それにはValueプロパティもしくはForceメソッドを使います。次のようになります。
 
> result.Value;;
val it : int = 10
 
> result.Force();;
val it : int = 10
 
個人的には、Forceの方が好きです。(やっぱりLazy(怠惰)に対してはForce(強いる)でしょ。)
 
さてそれでは、次はどうなるでしょうか。
 
>let k = 1 + lazy(x + 8);;
 
これはもちろんエラーになります。1はint型でLazy(x + 8)はLazy<int>型なので、足し算できないという訳です。
 
次はどうでしょう。
 
>let k = lazy (x + 3) + lazy (x + 8)
 
これもだめです。Lazy<int>型同士の和は定義されていないとエラーが返ります。
 
つぎはこれです。
 
> let k = lazy(lazy(x + 3));;
 
これは大丈夫で、結果は次の通りです。
 
val k : Lazy<Lazy<int>> = Value is not created.
 
これを働かせるには、2回Value(Force())する必要があります。
 
> k.Value.Value;;
val it : int = 6
 
このように、Lazyの殻に閉じこもってしまうと、一回ずつValue(Force())してやる必要があります。
 
さて次の関数の型がわかるでしょうか。
 
let test00 =
    (fun x ->
        lazy 
            (fun y ->
                (fun z -> x + y + z)))
                
調べてみます。
val test00 : int -> Lazy<(int -> int -> int)>
 
これはintを与えて、Valueしてやると、int -> int -> int型の関数を返すタイプの関数です。
では、x = 1 ,y = 2,z = 3で最後まで引数を与えて、それらの和を返すような形の式をかいてみてください。
 
> (test00 1).Value 2 3;;
val it : int = 6
 
となります。
 
もうひとつlazyな値を作る方法があります。それはLazy<'a>.Createを利用する方法です。
まずは型を調べてみます。
 
> Lazy<'a>.Create;;
val it : (unit -> 'a) -> System.Lazy<'a> = <fun:clo@5>
 
ということでunit -> 'a型の関数を渡せば、それをLazyの殻に包んで返してくれる関数のようです。
ただし、返り値はLazy<unit -> 'a>ではなくLazy<a'>ですから、殻をはずして()を与える必要なく、単に殻をはずせば、関数を実行して'aを返してくれるようです。
使ってみます。
 
> let x  = 3
let k = Lazy<int>.Create(fun () -> x * x );; //#1
 
val x : int = 3
val k : System.Lazy<int> = Value is not created.
 
> k.Value;;
val it : int = 9
 
ただ#1の部分は
let k = Lazy<_>.Create(fun () -> x * x )
として、型推論に任せた方が楽かと思います。
 
Lazy<>というのは一つの型ですから、いろいろに使えます。
たとえばLazy<int>型のリストを作ってみます。
 
> let k = [lazy(1+2);lazy(2*9);lazy(3+9)];;
 
val k : Lazy<int> list =
  [Value is not created.; Value is not created.; Value is not created.]
  
> (List.head k).Force();;
val it : int = 3
 
 
さて実際にどのような場合にLazy computationを使うかということですが、一般的にはとてもコストが高い処理をするときに、その処理をLazyにしておいて、必要に迫られたときだけValue(Force())するというようなときに有効です。
 
例えば重い処理を全部リストなり配列などにLazyでくるんで入れておいて、必要な分だけ取り出してForceするというような使い方です。
 
Functional Programming for the Real World: With Examples in F# and C#」という本には、画像ファイルを読み込んでプレビューを表示するという処理をLazyでくるんで配列(其々の要素が、異なる画像ファイルに対応)に入れておいて、選択されれば、その処理をForceするという例が載ってました。 
 
参考
 
次のような型注釈の形があるようです。(ノードが値をもつ二分木の例)
 
type LazyBinTree<'a> =
| Node of 'a * LazyBinTree<'a> Lazy * LazyBinTree<'a> Lazy
| Empty
 
F#Interactiveに食わせると次のように出てきます。
 
type LazyBinTree<'a> =
  | Node of 'a * Lazy<LazyBinTree<'a>> * Lazy<LazyBinTree<'a>>
  | Empty
スポンサーサイト

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

コメントの投稿

非公開コメント

typo

Lazy(x + 8) → lazy(x + 8)
思い処理→重い処理

No title

ありがとうございます。修正しました。
プロフィール

T GYOUTEN

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

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

この人とブロともになる

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