読者です 読者をやめる 読者になる 読者になる
ようこそ。睡眠不足なプログラマのチラ裏です。

ふと、Seq.tryFindの変な(誰得な)使い方を思いついた。F#でbreakとcontinue再び。

プログラミング F#2.0 F# 息抜き コンピューテーション式

以前、「F#で楽々breakとcontinue。継続モナドまじパネぇっす!」を書きました。
確かに楽々ではあるんですが、継続モナドとかマジで難しいですよ。
しかも、Visual Studioデバッグとかまともにできないですし...(´・ω・`)ショボーンな気持ちになっちゃいます。


F#でbreakとcontinue再び

ふと、Seq.tryFindの変な使い方を思いついちゃいました。
ループのbreakとcontinueっぽいものを表現するのに利用できるのではないか、と。


例えば、こんな風に書けます。「do! continue' else」のところがカッコワルイのはご愛嬌。

open System

printfn "%s" "----- for"
let hoge = 
  let x = ref "/(^o^)\"
  loop {for i in [1..10] do
          if i = 5 then
            printfn "%s" "five"
            do! continue' else 
          if i = 2 then
            printfn "%s" "two"
            do! continue' else
          printfn "%d" i
          if i = 7 then
            printfn "%s" "!!!"
            x := "\(^o^)/"
            return break'
            printfn "%d" i
          printfn "%s" "!" }
  !x
hoge |> printfn "%s"

printfn "%s" "----- while"
let fuga = 
  let x = ref "/(^o^)\"
  loop {let i = ref 0
        while !i < 6 do
          i := !i + 1
          if !i = 5 then
            printfn "%s" "five"
            do! continue' else
          if !i = 2 then
            printfn "%s" "two"
            do! continue' else
          printfn "%d" !i
          if !i = 7 then
            printfn "%s" "!!!"
            x := "\(^o^)/"
            return break'
            printfn "%d" !i
          printfn "%s" "!"}
  !x
fuga |> printfn "%s"
Console.ReadLine () |> ignore


実行結果

----- for
1
!
two
3
!
4
!
five
6
!
7
!!!
\(^o^)/
----- while
1
!
two
3
!
4
!
five
6
!
/(^o^)\

LoopBuilder

Seq.tryFindの使い方が変ですw optionの使い方が変ですw

// へぼいループビルダー
type LoopBuilder () =
  let while' gd body = 
    (fun _ ->
      let b = gd() 
      if b then
        if Option.isSome (body ()) then Some ()
        else body () |> (fun _ -> None)
      else 
        Some ()) |> Seq.initInfinite
  member this.While(gd,body) =
      while' gd body |> Seq.tryFind (fun x -> Option.isSome x) |> ignore
  member this.For (s, f) =
      s |> Seq.tryFind (fun x -> Option.isSome (f x)) |> ignore
  member this.Zero () = None
  member this.Combine (a,b) = a |> function
    |Some x -> Some x
    |_ -> b()
  member this.Return (x) = x 
  member this.ReturnFrom (x) = Some x 
  member this.Bind (m,f) = m |> function
    |Some x -> f x |> Some
    |_ -> None
  member this.Delay f = f 
  member this.Run f = f ()

let break' = Some ()
let continue' = None
let loop = LoopBuilder ()


わー!まったく難しいことをしていないシンプルな実装で、ループのbreakとcontinueな動作を表現できちゃったっぽいよ?
でも、やっぱり「do! continue' else」の部分がトテモカコワルイ。誰得かと(´・ω・`)


Imperative computation builder

ちゃんとカッコヨクやりたい人は、tomaspさんの「 Imperative computation builder 」あたりをあたった方が間違いなくよいです。
http://tomasp.net/blog/imperative-ii-break.aspx
http://fssnip.net/40



お知らせ

第61回CLR/H勉強会(TechParty2011)で、F# MVPのぶひささん( @nobuhisa_k )と、ASP.NET MVPさかもとさん( @jsakamoto )と、「F#パネルディスカッション 2011」に登壇します。
F#の魅力についてお話したいと思います。MVPな方2名にフルボッコにされる予定です。オフラインまたはオンラインでぜひぜひご参加ください。


CLR/H 公式ページ
http://clr-h.jp/

TechParty2011
http://techparty2011.iinaa.net/