スポンサーサイト

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

F#入門第49回(seq (2))

今回のお題は「seq (2)」です。
 
まずは前回の宿題の答えから
宿題
let nums = Seq.unfold (fun num ->
    if (num < 2 || (2 < num && num < 10) ) then Some(num.ToString(), num + 1) else None) 0
で出来上がるnumsはどんなものでしょうか。
(解説)
if の中身に留意してください。numが0,1,2と増えていくと2でNoneとなります。これが、追加を打ち切るサインですから、結局numが0,1に対するnum.ToString()の値だけがnumsの要素となるので答えは
seq ["0";"1"] となります。
 
さて、今回の話題はsequence expressionsです。
以前リスト内含表記というのを少しだけ紹介しましたが、あれはsequence expressionsのlistへの適用です。
それでは例をみてください。
 
> let k =
    seq{ for i in 1 .. 3 do
            printfn "now yield"  
            yield 2*i
        };;
 
val k : seq<int>
 
> k;;
now yield
now yield
now yield
val it : seq<int> = seq [2; 4; 6]
 
seq{ と }の間に通常のF#のコードと同様にコードを書くとyieldの部分で、seqに要素を押しこんで返してくれるというのがsequence expressionsです。
(mutableな値は使用できない(ref型は使えます)、内部で型は定義できないという制約はあります。)
 
ここで復習がてらリスト内含表記を一つ取り上げます。
 
> let u =
    [   for i in 1 ..3 do
            printfn "now  yield"
            yield 2*i
     ];;
 
now  yield
now  yield
now  yield
 
val u : int list = [2; 4; 6]
 
> u;;
val it : int list = [2; 4; 6]
 
ほぼ先ほどの例と同じですが、ちょっと違うところがあります。どこでしょうか。
そうです、リストでは定義したらすぐnow  yieldが3回続き、リストの内容まで表示されましたが、seqでは、定義しただけでは、now yieldは表示されず、sequenceの内容も表示されませんでした。k;;としたところで初めてnow yieldが表示されました。
実はsequenceは定義してすぐ計算されるのではなく、必要とされるときに、必要とされる部分までが計算されるのです。このような仕組みをlazy evaluation(遅延処理(評価))と言います
 
例えばSeq.take関数はsequenceから最初の何項かをとってきて、新しいsequenceを返す関数です。次の例では必要となる部分だけ計算されていることが分かると思います。
 
> Seq.take 1 k ;;
now yield
val it : seq<int> = seq [2]
 
> Seq.take 2 k ;;
now yield
now yield
val it : seq<int> = seq [2; 4]
 
このことを利用すると(見た目)無限数列が定義できます。次の例をみてください。
let examp =
    seq{    
            let rnd = new System.Random ()
            while true do
                yield rnd.Next(100)
            
        }
 
Randomは乱数を発生するクラスで、インスタンスrndを作成しrnd.Next(100)で0から99までの間の乱数を一つ返します。
上の例では無限ループになっていますが、実際に計算されるのは、必要となる分だけですから、無限ループにおちいることはありません。
例えば次のように使います。
 
> Seq.take 10 examp;;
val it : seq<int> = seq [36; 76; 71; 16; ...]
 
必要な分だけ必要な時にとってこれるというわけです。
 
yieldは普通のF#でコード部分では使えませんでしが、他にも普通のF#のコード部分では使えない、特別なキーワードyield!(yield bangと読みます。)があるのでこれを紹介しておきます。これはsequenceやlistやarray等のINumerable<>型のものに対して使うことができ、中身を取り出して要素にしてくれます。
分かりにくいと思いますので例を挙げます。
 
> let examp =
    seq{    yield! seq[1]
            yield! [2;3]
            yield! [|4|]
        };;
 
val examp : seq<int>
 
> examp;;
val it : seq<int> = seq [1; 2; 3; 4]
 
このyield!は再帰関数と組み合わせて、ツリー構造をフラットにするときなどに、よく使用されます。
 
簡単な例として乱数の列をyield!と再帰関数を使って作成してみます。
 
let examp =
    let rnd = new System.Random ()
    let rec Rnd =
        seq{ yield rnd.Next(100)
             yield! Rnd}    
    Rnd 
 
> Seq.take 10 examp;;
val it : seq<int> = seq [17; 75; 28; 6; ...]
 
一般にlistやarray等のINumerable<T>を実装しているものをF#では#seq<T>で表します。
例えばSeq.Concatという関数があります。これは
seq< #seq<'a> > -> seq<'a> 型として定義されています。
例えば次のように使います。
 
> let u = seq[[2];[3;4];[5]];;
val u : seq<int list> = [[2]; [3; 4]; [5]]
 
> let v = Seq.concat u;;
val v : seq<int> //遅延処理なので、まだ処理されていない
 
> v;;
val it : seq<int> = seq [2; 3; 4; 5] //中身だけが連結されてでてくる
 
#seq型に対して使えるので
let u = seq[[|2|];[|3;4|];[|5|]] 
としても
let u = seq[seq[2];seq[3;4];seq[5]] 
としても同様の操作が可能です。
スポンサーサイト

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

コメントの投稿

非公開コメント

宿題の答え

>3でNone
2でNone (2は含まれない)なので、
seq ["0"; "1"]

No title

すいません。恥ずかしい間違いをしておりました。訂正しました。
プロフィール

T GYOUTEN

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

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

この人とブロともになる

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