Let's begin F#
Let's begin F#
Let's begin F#
ATOMRSS
部屋に参加

トピック:チラ裏.fs

いげ太

「日記に書かずにここに書け!」がスローガン。サンプル コードを共有しよう。

  • アクセス数:482件
  • コメント数:15件
  • codeなにがしブックマークに追加する 0 users
  • このページを del.icio.us に追加
  • このページをはてなブックマークに追加

コメント

Seq.append は seq を連結する。

> let xs, ys = seq { 0..1 }, seq { 2..3 };;

val ys : seq<int>
val xs : seq<int>

> Seq.append xs ys;;
val it : seq<int> = seq [0; 1; 2; 3]
演算子を作ってみるのもまた一興。
> let (@@) = Seq.append;;

val ( @@ ) : (seq<'a> -> seq<'a> -> seq<'a>)

> xs @@ ys;;
val it : seq<int> = seq [0; 1; 2; 3]

GJ

seq 関数はいわばアップ キャスト。

> [1; 2; 3];;
val it : int list = [1; 2; 3]
> seq [1; 2; 3];;
val it : seq<int> = [1; 2; 3]
> [|1; 2; 3|];;
val it : int array = [|1; 2; 3|]
> seq [|1; 2; 3|];;
val it : seq<int> = [|1; 2; 3|]
ちなみに、seq 関数は Microsoft.FSharp.Core.Pervasives モジュールに定義されている。

GJ

Seq.concat は平坦化(flatten)。seq の seq のネストを解いて seq にする。

> let xss = seq [ seq{0..1}; seq{2..3}; seq{4..5} ];;

val xss : seq<seq<int>>

> Seq.concat xss;;
val it : seq<int> = seq [0; 1; 2; 3; ...]
seq<#seq<'a>> -> seq<'a> という型を持つので、要素がリストとかでも OK。
> Seq.concat [[9;8;7]; [6;5;4]; [3;2;1]];;
val it : seq<int> = seq [9; 8; 7; 6; ...]

GJ

目新しいところも。CTP では多くの関数が追加されている。distinct もその一つ。重複した要素を取り除く。

> Seq.distinct [1; 4; 1; 4; 2; 1; 3; 5; 6];;
val it : seq<int> = seq [1; 4; 2; 3; ...]
重複判定に使うキー値を任意に導出したいなら、distinct_by が使える。
> let alist = [1,"one"; 2,"two"; 2,"to"; 3,"three"];;

val alist : (int * string) list

> Seq.distinct_by fst alist;;
val it : seq<int * string> = seq [(1, "one"); (2, "two"); (3, "three")]
重複要素が存在すると、後の要素が消され、先の要素が生きる。

GJ

先頭から 要素いらない、という場合は skip を。これで tail が取れねーと悩むこともなくなった、かな。

> Seq.skip 1 ["list"; "1"; "2"; "3"];;
val it : seq<string> = seq ["1"; "2"; "3"]
条件が成立する間スキップするには skip_while を使う。
> Seq.skip_while ((>) 10) [1; 2; 11; 12; 4; 5];;
val it : seq<int> = seq [11; 12; 4; 5]
条件が true の間はとばして、条件が false になったとこから後ろが返される。

GJ

skip の逆、つまり、先頭から 要素だけほしい、という場合は take だ。

> Seq.take 3 [0; 1; 2; 3; 4; 5];;
val it : seq<int> = seq [0; 1; 2]
seq の要素数が 未満である場合はエラーになる。これは skip でも同じくエラーになる。
> Seq.take 3 [0; 1];;
val it : seq<int>
= Error: Seq.take: the input sequence had insufficient elements
パラメータ名: n
> Seq.skip 3 [0; 1];;
val it : seq<int>
= Error: Seq.skip: the input sequence had insufficient elements
パラメータ名: n
しかし、take には、エラーを出さない「取れるだけ取ってこいよ版」が存在する。truncate だ。
> Seq.truncate 3 [0; 1];;
val it : seq<int> = seq [0; 1]
条件が成立する間テイクするには take_while を使う。
> Seq.take_while ((>) 10) [1; 2; 11; 12; 4; 5];;
val it : seq<int> = seq [1; 2]

GJ

つまりは SQL の Where(選択)。条件に一致する要素のみを取り出すフィルタ。

> Seq.filter ((>) 10) [1; 2; 11; 12; 4; 5];;
val it : seq<int> = seq [1; 2; 4; 5]

GJ

つまりは SQL の Select(射影)。すべての要素に関数を適用する。

> Seq.map ((*) 2) [10; 10; 2; 3; 10];;
val it : seq<int> = seq [20; 20; 4; 6; ...]

GJ

「SELECT ... FROM ... WHERE ...」はよくある形。choose で、filter と map をいっぺんに。

> let family = seq ["Sazae",   "Fuguta", 24;
-                   "Masuo",   "Fuguta", 28;
-                   "Katsuo",  "Isono",  11;
-                   "Wakame",  "Isono",   9;
-                   "Tarao",   "Fuguta",  3;
-                   "Namihei", "Isono",  54;
-                   "Fune",    "Isono",  52];;

val family : seq<string * string * int>

> Seq.choose (fun (fnm,snm,age) ->
-              if age <= 12 then Some (snm + " " + fnm) else None)
-            family;;
val it : seq<string> = seq ["Isono Katsuo"; "Isono Wakame"; "Fuguta Tarao"]

GJ

つの seq を一緒に回す版 map で map2。またの名を zipWith。同じ位置にある要素をがっちゃんこして つの seq を混ぜ合わせる。

> Seq.map2
-   (fun fnm snm -> snm + " " + fnm)
-   (seq ["Namihei"; "Fune"; "Katsuo"; "Wakame"; "Masuo"; "Sazae"; "Tarao"])
-   (seq ["Isono"; "Isono"; "Isono"; "Isono"; "Fuguta"; "Fuguta"; "Fuguta"]);;
val it : seq<string>
= seq ["Isono Namihei"; "Isono Fune"; "Isono Katsuo"; "Isono Wakame"; ...]
ただ単にタプルにする(直積を取る)ってだけなら zip で。
> Seq.zip
-   (seq ["Namihei"; "Fune"; "Katsuo"; "Wakame"; "Masuo"; "Sazae"; "Tarao"])
-   (seq ["Isono"; "Isono"; "Isono"; "Isono"; "Fuguta"; "Fuguta"; "Fuguta"]);;
val it : seq<string * string>
= seq
    [("Namihei", "Isono"); ("Fune", "Isono"); ("Katsuo", "Isono");
     ("Wakame", "Isono"); ...]

GJ

あー >>9 と >>10 で seq 関数使ったのは冗長か。特に必要なかったなー。

GJ

map して concat するから map_concat。またの名を SelectMany。map に与える関数が seq を返す関数だと、map の結果は seq の seq になるわけだけど、じゃなくて平坦化した seq を返してもらいたいってときはこっちを使う。

> Seq.map_concat string (seq {10..20});;
val it : seq<char> = seq ['1'; '0'; '1'; '1'; ...]

GJ

「折り畳み」と呼ばれる操作。初期値に を与え、関数 (+) で [1;2;3;4;5] を折り畳むとき、((((0 1) 2) 3) 4) が評価される。fold によってこれを行う。

> Seq.fold (+) 0 (seq {1..10});;
val it : int = 55
> Seq.fold (+) 0 Seq.empty;;
val it : int = 0
初期値を必要としないなら reduce が使える。ただし、空の seq を渡すと例外に。
> Seq.reduce (+) (seq {1..10});;
val it : int = 55
> Seq.reduce (+) Seq.empty;;
System.ArgumentException: the input sequence was empty
パラメータ名: e
   場所 Microsoft.FSharp.Collections.Seq.reduce[A](FastFunc`2 f, IEnumerable`1 i
e)
   場所 <StartupCode$FSI_0003>.$FSI_0003._main()
stopped due to error
reduce の評価値は seq の要素値と同じ型になるが、fold はそうでなくともよく、初期値と同じ型になる。reduce の初期値は seq の先頭要素である、と見るのがよいか。
> Seq.reduce;;
val it : (('a -> 'a -> 'a) -> seq<'a> -> 'a) = <fun:clo@0>
> Seq.fold;;
val it : (('a -> 'b -> 'a) -> 'a -> seq<'b> -> 'a) = <fun:clo@0_1>
> Seq.fold (fun n (s:string) -> n + s.Length) 0 ["foo"; "bar"; "baz"];;
val it : int = 9
ちなみに、(Seq.fold (+) 0) 相当の sum は既に定義済み。
> Seq.sum (seq {1..10});;
val it : int = 55
> Seq.sum (Seq.empty : seq<int>);;
val it : int = 0

GJGJ

on-demand な fold/reduce。scan/scan1 は、途中経過を返しながら折り畳む。

> Seq.scan (+) 0 (seq {1..10})
- |> Seq.to_list;;
val it : int list = [0; 1; 3; 6; 10; 15; 21; 28; 36; 45; 55]
> Seq.scan1 (+) (seq {1..10})
- |> Seq.to_list;;
val it : int list = [3; 6; 10; 15; 21; 28; 36; 45; 55]
scan では先頭要素に初期値を返すが、scan1 は最初の演算結果を返すことに注意。

余談。CTP より前の version では、fold/reduce は fold/fold1 という名前であり、それに対応して scan/scan1 があった。しかし、いまや fold1 は reduce と改名された。scan/scan1、特に scan1 は、いずれ名前が変更されるのではないかと予想される。

GJGJ

fold の逆で unfold。いわば漸化式。初期値を元に、それに関数を適用して次の要素、それにまた関数を適用してそのまた次の要素、と次々に要素値を算出していく。

> let xs = Seq.unfold (fun x -> if x <= 1024 then Some (x, 2*x) else None) 1;;

val xs : seq<int>

> Seq.to_list xs;;
val it : int list = [1; 2; 4; 8; 16; 32; 64; 128; 256; 512; 1024]
等比数列、等差数列だとありがたみがすくないから、端的な例というと結局フィボナッチ数列になっちゃう。
> Seq.unfold (fun (a1,a2) -> Some (a1, (a2,a1+a2))) (1,1)
- |> Seq.take 10
- |> Seq.to_list;;
val it : int list = [1; 1; 2; 3; 5; 8; 13; 21; 34; 55]
unfold は無限への入り口。

GJGJ

前へ 1 次へ

コメントする

[block]から[/block]までの範囲はブロック表示されます。
部分的に目立たせたい時や、引用などにお使いください。

[code]から[/code]までの範囲は等幅表示されます。
ソースコードや設定ファイルの記述などにお使いください。

コメントする権限がありません