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

トピック:雑談.fs

いげ太

なんでもどうぞ。とりあえず F# 部屋 コ目のトピックとして立ててみた。

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

コメント

たとえば、
    popped [0; 1; 2; 3]
としたときに、
    [(0, [1; 2; 3]); (1, [0; 2; 3]); (2, [0; 1; 3]); (3, [0; 1; 2])]
を得る関数、popped を作りたい。

// list 再帰版
let popped lst =
    let rec popped' ret fr bk =
      match bk with
        []    -> ret
      x::xs -> popped' ((x, List.append (List.rev fr) xs)::ret)
                         (x::fr) xs
    in
    popped' [] [] lst |> List.rev;;

// Seq.unfold 
let popped lst =
    Seq.unfold
      (fun (fr, bk) -> match bk with
                         []    -> None
                       x::xs -> Some ((x, List.append (List.rev fr) xs),
                                        (x::fr, xs)))
      ([], lst);;

うーん。他にいい方法はないものか。
インデントがズレる...

  • GoodJob
  • 0

forum 275 がやっとできた。

すべての文字を各一回使ってできる文字列のパターン
http://code.nanigac.com/forum/view/275

let popped (l1, l2) =
   let rec popped' ret fr bk =
     match bk with
       []    -> ret
     x::xs -> popped' ((x::l1, List.append (List.rev fr) xs)::ret)
                        (x::fr) xs
   in
   popped' [] [] l2
   |> List.rev;;

let all_comb =
    let rec all_comb' function
        (_, [])::_ as lst -> List.map (List.rev << fst) lst
      lst -> all_comb' (List.map_concat popped lst)
    in
    all_comb' [([], l)];;

// test run
all_comb [0;1;2;3];;
val it int list list
[[0; 1; 2; 3]; [0; 1; 3; 2]; [0; 2; 1; 3]; [0; 2; 3; 1]; [0; 3; 1; 2];
   [0; 3; 2; 1]; [1; 0; 2; 3]; [1; 0; 3; 2]; [1; 2; 0; 3]; [1; 2; 3; 0];
   [1; 3; 0; 2]; [1; 3; 2; 0]; [2; 0; 1; 3]; [2; 0; 3; 1]; [2; 1; 0; 3];
   [2; 1; 3; 0]; [2; 3; 0; 1]; [2; 3; 1; 0]; [3; 0; 1; 2]; [3; 0; 2; 1];
   [3; 1; 0; 2]; [3; 1; 2; 0]; [3; 2; 0; 1]; [3; 2; 1; 0]]

  • GoodJob
  • 0

はじめまして、いげ太さんのブログ読ませてもらってます。
let poped lst  [for in lst -> (i,[for in lst when i<> -> j])]
でいかがでしょう。

GJ

うお~すげぇ!さくっとワンラインで、効率的かつわかりやすい!!
そうか。ネストさせればよかったんですね。レスありがとうございます!

ちなみに、内包表記を展開してみるとこうですね。

let popped lst = List.map (fun i -> (i, List.filter ((<>) i) lst)) lst;;

  • GoodJob
  • 0

T_GYOUTEN さんのコメントを受けて、forum 275 を改良してみた(^^

let all_comb lst =
    let rec all_comb' lst =
        match snd << List.hd <| lst with
          [] -> List.map (List.rev << fst) lst
        | _  -> [ for l1, l2 in lst ->>
                  [ for x in l2 -> (x::l1, [ for y in l2 when x <> y -> y ]) ]]
                |> all_comb'
    in
    all_comb' [([], lst)];;
効率を考えるなら List.rev なしで。なんとなくしっくりくる順番になるかなーくらいの理由で、入れてあります。

GJ

私もforum 275 をやってみました。(結構苦労しました。)
let all_comb lst              
 let rec sub_all_c lh lt ltremain res =
  if lt [] then res [lh]
  else
    match ltremain with
    [] -> res
    :: ->let t_l [for in lt when <>  -> j]
               let t_r =sub_all_c (lh@[h]) t_l t_l res              
               sub_all_c lh lt t_r 
 sub_all_c [] lst lst [] 

  • GoodJob
  • 0

>6 を解読するのにかなり苦労しました(汗
といっても、おおまかな流れしかわかってませんが(汗汗

let all_comb lst =
    let rec sub_all_c lh lt ltremain res =
        printfn "%A - %A" res lh; // -> 再帰の流れを観察する
        if lt = [] then res @ [lh]
                   else
                     match ltremain with
                     | [] -> res
                     | h :: t ->let t_l = [for j in lt when j <> h  -> j] in
                                let t_r =sub_all_c (lh@[h]) t_l t_l res in
                                sub_all_c lh lt t t_r 
    in
    sub_all_c [] lst lst [];;

let _ = all_comb [0..2];;
print を差し込んで観察してみた感じだと、lh が現在探索中のノード位置で、res が探索済みのパスを保持するスタックな、深さ優先探索でしょうか?

>5 などで書いたヤツは幅優先探索なので、僕も深さ優先なヤツもこしらえようと思っていたんですが、方法が思い浮かばないでいました。解読するのでさえやっとなので、こりゃ独力では思いつけないわけです(^^;;

  • GoodJob
  • 0

あっ。わかりにくいところがあったかもしれないので補足させてください。

いきなり深さ優先探索とか幅優先探索とか言い出しましたが、これは、forum 275 を解くことってつまり木構造の探索だよね、という視点からの発言です。また、元ネタは文字列を対象とした話なのですが、リスト的なものに対する汎用な処理として解きたかったので、勝手ながら、ここでは list に対する処理として記述させてもらいました。

ちなみに、all_comb [0..3] の場合のツリーはこんな感じですね。

[] -+- [0] -+- [0; 1] -+- [0; 1; 2] - [0; 1; 2; 3]
    |       |          +- [0; 1; 3] - [0; 1; 3; 2]
    |       |
    |       +- [0; 2] -+- [0; 2; 1] - [0; 2; 1; 3]
    |       |          +- [0; 2; 3] - [0; 2; 3; 1]
    |       |
    |       +- [0; 3] -+- [0; 3; 1] - [0; 3; 1; 2]
    |                  +- [0; 3; 2] - [0; 3; 2; 1]
    |
    +- [1] -+- [1; 0] -+- [1; 0; 2] - [1; 0; 2; 3]
    |       |          +- [1; 0; 3] - [1; 0; 3; 2]
    |       |
    |       +- [1; 2] -+- [1; 2; 0] - [1; 2; 0; 3]
    |       |          +- [1; 2; 3] - [1; 2; 3; 0]
    |       |
    |       +- [1; 3] -+- [1; 3; 0] - [1; 3; 0; 2]
    |                  +- [1; 3; 2] - [1; 3; 2; 0]
    |
    +- [2] -+- [2; 0] -+- [2; 0; 1] - [2; 0; 1; 3]
    |       |          +- [2; 0; 3] - [2; 0; 3; 1]
    |       |
    |       +- [2; 1] -+- [2; 1; 0] - [2; 1; 0; 3]
    |       |          +- [2; 1; 3] - [2; 1; 3; 0]
    |       |
    |       +- [2; 3] -+- [2; 3; 0] - [2; 3; 0; 1]
    |                  +- [2; 3; 1] - [2; 3; 1; 0]
    |
    +- [3] -+- [3; 0] -+- [3; 0; 1] - [3; 0; 1; 2]
            |          +- [3; 0; 2] - [3; 0; 2; 1]
            |
            +- [3; 1] -+- [3; 1; 0] - [3; 1; 0; 2]
            |          +- [3; 1; 2] - [3; 1; 2; 0]
            |
            +- [3; 2] -+- [3; 2; 0] - [3; 2; 0; 1]
                       +- [3; 2; 1] - [3; 2; 1; 0]

  • GoodJob
  • 0

6についてですが、分かりにくいコードで申し訳ないです。
書いた本人でも時々わからなくなります。もっとすっきり書けないものかと思ってます。
補足説明させていただきます。
リストが与えられた時、最初にlhにどれを選ぶかを設定します。選ばれていないのがltに入ります。lhとltが深さを表します。(ふたつ合わすと全体と一致します。)
よってsub_all_c (lh@[h]) t_l t_l resの部分で(t_lはltからhを除いたリスト)、hを選択して、深さを一つ進めるという作業になっています。
それぞれの深さのindex代わりを務めるのが、ltremainで、同じ深さで、横方向(?)の移動に使います。sub_all_c  lh lt t_rの部分の呼び出しがそれを担当します。
最後の引数は結果を付け加えていくための、結果保存用の変数です。
深さ方向で選ぶものがなくなった時に res [lh] という形で、結果に付け加えてます。
ちなみに、深さ方向で選ぶものがなくなった時に結果に加える代わりに、深さ方向で、えらぶものが特定の個数になったら、結果に加えて、深さ方向へ進むのをやめると、特定の個数を選んだ順列を取り出す関数に変身します。
例えば次のようになります。

let all_comb2 lst len               
 let rec sub_all_c lh lt ltremain res 
  if (List.length lh len then res [lh] 
  else 
      match ltremain with 
      [] -> res 
      :: ->let t_l [for in lt when <>  -> j] 
                 let t_r =sub_all_c (lh@[h]) t_l t_l res   
                 sub_all_c lh lt t_r  
 sub_all_c [] lst lst [] 

これでall_conb2 [1;2;3] 2とすると
int list list [[1; 2]; [1; 3]; [2; 1]; [2; 3]; [3; 1]; [3; 2]]
と表示されます。
再帰は奥が深いなーと思っているこの頃です。

  • GoodJob
  • 0

蛇足ながら、思いついたことがあったので、付け加えさせてもらいます。
上の9のコードで、[for in lt when <>  -> j] の部分を
[for in lt when  -> j]に直すと、比較可能な要素からなるリストを与えると、
組み合わせをリストにくるんで返す関数になります。

let all_comb3 lst len               
  let rec sub_all_c lh lt ltremain res 
   if ((List.length lh) len then res [lh] 
   else 
      match ltremain with 
      [] -> res 
      :: ->let t_l [for in lt when  -> j] 
                 let t_r =sub_all_c (lh@[h]) t_l t_l res   
                 sub_all_c lh lt t_r  
  sub_all_c [] lst lst []  

とすると、

all_comb3 [1;2;3;4;5] ;;で

val it int list list
[[1; 2]; [1; 3]; [1; 4]; [1; 5]; [2; 3]; [2; 4]; [2; 5]; [3; 4]; [3; 5];
   [4; 5]]
が返ります。

  • GoodJob
  • 0

蛇足の蛇足でもう一つ付け加えさせてください。
リスト6の
match ltremain with 
   [] -> res 
の部分を
match ltremain with 
      [] -> res [lh]
と置き換えると、何個かを選んで並べた順列すべてを得る関数となります。

例えば
let all_comb4 lst                
  let rec sub_all_c lh lt ltremain res 
   if (lt [] then res [lh] 
   else
      match ltremain with 
      [] -> res [lh] 
      :: ->let t_l [for in lt when <>  -> j] 
                 let t_r =sub_all_c (lh@[h]) t_l t_l res   
                 sub_all_c lh lt t_r  
  sub_all_c [] lst lst [] 
において
all_comb4 [1;2;3];;で

val it int list list
[[1; 2; 3]; [1; 2]; [1; 3; 2]; [1; 3]; [1]; [2; 1; 3]; [2; 1]; [2; 3; 1];
   [2; 3]; [2]; [3; 1; 2]; [3; 1]; [3; 2; 1]; [3; 2]; [3]; []]
が返ります。
さらに先ほどと同様にh<>jの部分をh<jに置き換えると
0個以上の組み合わせをすべて返す関数ができます。

let all_comb5 lst                
  let rec sub_all_c lh lt ltremain res 
   if (lt [] then res [lh] 
   else
      match ltremain with 
      [] -> res [lh] 
      :: ->let t_l [for in lt when  -> j] 
                 let t_r =sub_all_c (lh@[h]) t_l t_l res   
                 sub_all_c lh lt t_r  
  sub_all_c [] lst lst [] 

でall_comb5 [1;2;3] ;;とすると
val it int list list
[[1; 2; 3]; [1; 2]; [1; 3]; [1]; [2; 3]; [2]; [3]; []]
となります。






  • GoodJob
  • 0

ご説明どうもです!しかし悲しいかな、全然頭がついていかない…(^^;;
T_GYOUTEN さんご説明で、おおよそのところはわかってきたのですが、でもコードを追えっていわれたら追えない、みたいな感じです。なので、とりあえず all_comb*N* シリーズを自分流にまとめてみました。

/// n 個から n 個選んだ順列
let perm_get_n lst =
    let rec f (h,t) brd res =
        match t, brd with
          [], _    -> res @ [h]
        | _, []    -> res
        | _, x::xs -> let ys = [ for y in t when y <> x  -> y ] in
                      f (h,t) xs (f (h@[x],ys) ys res) 
    in
    f ([],lst) lst [];;

/// n 個から m 個選んだ順列
let perm_get_m lst len =
    let rec f (h,t) brd res =
        match brd with
          _ when List.length h = len -> res @ [h]
        | []     -> res
        | x::xs  -> let ys = [ for y in t when y <> x  -> y ] in
                       f (h,t) xs (f (h@[x],ys) ys res) 
    in
    f ([],lst) lst [];;

/// n 個から m 個選んだ組合せ
let comb_get_m lst len =
    let rec f (h,t) brd res =
        match brd with
          _ when List.length h = len -> res @ [h]
        | []     -> res
        | x::xs  -> let ys = [ for y in t when y > x  -> y ] in
                       f (h,t) xs (f (h@[x],ys) ys res) 
    in
    f ([],lst) lst [];;

/// n 個から m 個選ぶ順列において、可能なすべての順列を得る
let perm_every lst =
    let rec f (h,t) brd res =
        match t, brd with
          [], _ | _, []  -> res @ [h]
        | _, x::xs       -> let ys = [ for y in t when y <> x  -> y ] in
                            f (h,t) xs (f (h@[x],ys) ys res) 
    in
    f ([],lst) lst [];;

/// n 個から m 個選ぶ組合せにおいて、可能なすべての組合せを得る
let comb_every lst =
    let rec f (h,t) brd res =
        match t, brd with
          [], _ | _, []  -> res @ [h]
        | _, x::xs       -> let ys = [ for y in t when y > x  -> y ] in
                            f (h,t) xs (f (h@[x],ys) ys res) 
    in
    f ([],lst) lst [];;
> perm_get_n [1..3];;
val it : int list list
= [[1; 2; 3]; [1; 3; 2]; [2; 1; 3]; [2; 3; 1]; [3; 1; 2]; [3; 2; 1]]
> perm_get_m [1..4] 2;;
val it : int list list
= [[1; 2]; [1; 3]; [1; 4]; [2; 1]; [2; 3]; [2; 4]; [3; 1]; [3; 2]; [3; 4];
   [4; 1]; [4; 2]; [4; 3]]
> comb_get_m [1..4] 2;;
val it : int list list = [[1; 2]; [1; 3]; [1; 4]; [2; 3]; [2; 4]; [3; 4]]
> perm_every [1..3];;
val it : int list list
= [[1; 2; 3]; [1; 2]; [1; 3; 2]; [1; 3]; [1]; [2; 1; 3]; [2; 1]; [2; 3; 1];
   [2; 3]; [2]; [3; 1; 2]; [3; 1]; [3; 2; 1]; [3; 2]; [3]; []]
> comb_every [1..3];;
val it : int list list
= [[1; 2; 3]; [1; 2]; [1; 3]; [1]; [2; 3]; [2]; [3]; []]
もうちょっとにらめっこしてみようかと思います。

  • GoodJob
  • 0

>6 やっと理解しました。
ところで、append はコスト高なので、以下のようにしたほうが速度面で有利ですね。

let perm_get_n lst =
    let rec f xs ys crr res =
        match xs, ys with
          _, [] -> crr::res
        | [], _ -> res
        | x::xs, _ -> let zs = List.filter ((<>) x) ys in
                      f xs ys crr <| f zs zs (x::crr) res
    in
    f lst lst [] [] |> List.map List.rev |> List.rev;;
以下のようなコードでベンチマークを取ってみるとわかりよいかと。
let benchmark f =
    let sw  = new System.Diagnostics.Stopwatch() in
    sw.Start(); f () |> ignore; sw.Stop();
    sw.Elapsed;;

// ex:
// (benchmark (fun () -> perm_get_n [0..7])).ToString();;

  • GoodJob
  • 0

ベンチマークとってみました、appendを使う方では 約57.5秒、13では0.16秒
実に360倍ほど違いがありました。へたな場所でappendを使うと、寿命がつきてしまいますね。
私はC#をずっと使ってきて、現在F#を勉強中なのですが、こいつはおもしろいですね。

  • GoodJob
  • 0

クイックソートを書いてみた。Haskell チックに。

let rec qsort = function []   -> []
                       | h::t -> qsort [for x in t when x <  h -> x]
                               @ [h]
                               @ qsort [for x in t when h <= x -> x];;
あまり速そうに見えない。いや、実際計測したわけじゃないですが、なんとなく。見た目的に。F# 的に。
もうちょっと実装を工夫したほうがいいのかな?

GJ

マージソートも書いてみた。こんな感じかな。

let msort lst =
    let rec marge f = function x::y::ys -> f (x, y) :: marge f ys
                             | xs       -> xs
    in
    let rec sort = function xs, [] | [], xs          -> xs
                          | x::xs, y::ys when x <= y -> x :: sort (xs, y::ys)
                          | x::xs, y::ys             -> y :: sort (x::xs, ys)
    in
    let rec msort' = function _::_::_ as xs -> msort' << marge sort <| xs
                            | xs            -> xs
    in
    match msort' [for x in lst -> [x]] with [] -> [] | x::_ -> x;;

GJGJ

何か変なエラー??っぽいのがあるのですが、既知のバグ(?)なのでしょうか?
hubFSとかは、追えてません><
まだ、最新では試せてなく、バージョンは1.9.4.17です。

let test (d:decimal) -d 

internal error: trait constraint was resolved to F# library intrinsic in codegen. Please build small example that reproduces this problem and report it to fsbugs@microsoft.com

となるのです。

これは……内部エラー(?)だからMSにレポートしろよー、って、ことですよね??

ちなみに
let test (d:decimal) System.Decimal.Negate(d)
だと平気なんですが。

  • GoodJob
  • 0

ほとんど憶測で書きますが。。
(調査に関しては最新の 1.9.4.19 でしています)

たとえば、F# において、

let add (x:T) (y:T) = x + y;;
と書いた場合、
let add (x:T) (y:T) = T.op_Addition(x, y);;
と変換されます。

しかしながら、int32 やら float やらは op_Addition メソッドを外部に公開していません。さて困った、これでは int32 や float に対して (+) が使えません。ということで、F# では、Member Constraints なる機能によってこれを回避します。(+) 演算子は、prim-types.fs で以下のように定義されています。
let inline (+) (x: ^a) (y: ^b) : ^c = 
     ((^a or ^b): (static member (+) : ^a * ^b -> ^c) (x,y))
     when ^a : int32       and ^b : int32      = (# "add" x y : int32 #)
     when ^a : float       and ^b : float      = (# "add" x y : float #)
     ...
ざっと説明すると、これは引数の型による条件分岐です。つまり、引数の型が int32 やら float やらだった場合にはそれ用の式を、それ以外の場合には op_Addition を使いましょう、ということです。

(+) の定義では、Member Constraints の対象となる型として、decimal を含みません。ですが decimal の 演算は正しく行われます。すなわち、decimal が op_Addition を公開していて、+ 演算が op_Addition に変換されるために正しく動作するわけです。

問題は (~-) です(プレフィクスの "-" は、(~-) 演算子として prim-types.fs に定義されています)。(~-) の Member Constraints には decimal が含まれていません。このことと yuji1982 さんの事例とから考えるに、(~-) は op_UnaryNegation を使用しないのだろうと推察されます。

ここで、対応する演算子である (~+) を見てみると、Member Constraints に decimal を含んだ定義がされています。(~+) の Member Constraints に decimal が含まれているのは、(~+) が op_UnaryPlus を使わないからでしょう。であれば、なぜ、op_UnaryNegation を使用しない (~-) は、Member Constraints の対象型として decimal を含まないのか。

以上から、僕はこの挙動がバグかもしれないと疑います。

ただ、気になるのはエラー メッセージですね。「does not support any operators named '~-'」と出てもよさそうに思えますが。もしかしたら、バグじゃなくて、なにか別の理由があっての挙動なのか。。。

[参考]

http://research.microsoft.com/fsharp/manual/spec2.aspx#_T...

GJ

>>17

let test (d:decimal) -d 
CTP (version 1.9.6.0) で直ってますねー。

何か変なエラー??っぽいのがあるのですが、既知のバグ(?)なのでしょうか?
バグで正解だったみたいですね(^^

  • GoodJob
  • 0

比較くらいなら慣れの問題で。それぞれ逆向きの演算子があるから困ることもない。

> ((<) 10) 1;;
val it : bool = false
> ((>) 10) 1;;
val it : bool = true
しかし、たとえば割り算ではどうか。
> ((/) 10.0) 5.0;;
val it : float = 2.0
(/) に対して「割られる数」を部分適用したいときはどうすればいいか。
> (fun x -> x / 10.0) 5.0;;
val it : float = 0.5
いちいち無名関数を作るのはめんどい。ということで、こんなん用意してみる。
> let __ f x y = f y x;;

val __ : ('a -> 'b -> 'c) -> 'b -> 'a -> 'c

するとこう書ける。
> (__ (<) 10) 1;;
val it : bool = true
> (__ (/) 10.0) 5.0;;
val it : float = 0.5
placeholder がほしい。

  • GoodJob
  • 0

tupled form を curried form に変型させる FuncFromTupled メソッドってのがあるんですが。

> let f = FuncConvert.FuncFromTupled (DateTime.ParseExact : _ * _ * _ -> _);;

  let f = FuncConvert.FuncFromTupled (DateTime.ParseExact : _ * _ * _ -> _);;
  --------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stdin(3,9): error FS0041: The method 'FuncFromTupled' is overloaded. Possible ma
tches are shown below (or in the Error List window).


Possible overload: 'static member FuncConvert.FuncFromTupled : ('a * 'b * 'c ->
'd) -> ('a -> 'b -> 'c -> 'd)'.


Possible overload: 'static member FuncConvert.FuncFromTupled : ('a -> 'b) -> ('a
 -> 'b)'.
もうちょっと型推論くんにがんばってほしかったりするのですが、こうするしかないのかな。
> let f : string -> string -> IFormatProvider -> DateTime =
-     FuncConvert.FuncFromTupled (DateTime.ParseExact : _ * _ * _ -> _);;

val f : (string -> string -> IFormatProvider -> DateTime)
というか、('a -> 'b) -> ('a -> 'b) な FuncFromTupled の必要性が疑問です。

  • GoodJob
  • 0

前へ 1 次へ

コメントする

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

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

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