29 people like it.

Enumerator computation builder

The snippet defines computation builder for working with IEnumerator. The bind operation (let!) reads next element from the enumerator, so the computation can be used for expressing things that cannot be written just using seq.

Module with basic functions for working with IEnumerator

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
module Enumerator = 

  let single a = 
    let state = ref 0
    { new IEnumerator<_> with 
        member self.Current = if (state.Value = 1) then a else invalidOp "!"
      interface System.Collections.IEnumerator with
        member self.Current = if (state.Value = 1) then box a else invalidOp "!"
        member self.MoveNext() = state := state.Value + 1; state.Value = 1
        member self.Reset() = state := 0
      interface System.IDisposable with 
        member self.Dispose() = ()}

  let combine (a:IEnumerator<_>) b = 
    let current = ref a
    let first = ref true
    { new IEnumerator<_> with 
        member self.Current = current.Value.Current
      interface System.Collections.IEnumerator with
        member self.Current = box current.Value.Current
        member self.MoveNext() = 
          if current.Value.MoveNext() then true 
          elif first.Value then 
            current := b
            first := false
            current.Value.MoveNext()
          else false
        member self.Reset() = 
          a.Reset(); b.Reset()
          first := true; current := a
      interface System.IDisposable with 
        member self.Dispose() = a.Dispose(); b.Dispose() }
  
  let zero () = 
    { new IEnumerator<_> with 
        member self.Current = invalidOp "!"
      interface System.Collections.IEnumerator with
        member self.Current = invalidOp "!"
        member self.MoveNext() = false
        member self.Reset() = ()
      interface System.IDisposable with 
        member self.Dispose() = ()}
  
  let delay f = 
    let en : Lazy<IEnumerator<_>> = lazy f()
    { new IEnumerator<_> with 
        member self.Current = en.Value.Current
      interface System.Collections.IEnumerator with
        member self.Current = box en.Value.Current
        member self.MoveNext() = en.Value.MoveNext()
        member self.Reset() = en.Value.Reset()
      interface System.IDisposable with 
        member self.Dispose() = en.Value.Dispose() }

  let toSeq gen = 
    { new IEnumerable<'T> with 
          member x.GetEnumerator() = gen() 
      interface IEnumerable with 
          member x.GetEnumerator() = (gen() :> IEnumerator) }

Computation builder for working with enumerators

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
type EnumerableBuilder() = 
  member x.Delay(f) = Enumerator.delay f
  member x.YieldFrom(a) = a
  member x.Yield(a) = Enumerator.single a
  member x.Bind(a:IEnumerator<'a>, f:_ -> IEnumerator<'b>) =
    if (a.MoveNext()) then f (Some(a.Current)) else f(None) 
  member x.Combine(a, b) = Enumerator.combine a b
  member x.Zero() = Enumerator.zero ()

let iter = new EnumerableBuilder()    

Examples of using 'iter' computations

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
// Enumerator that produces 3 numbers
let e = iter { yield 1; yield 2; yield 3 }
e.MoveNext()
e.Current

// Enumerator that iumplements "identity" and prints elements
let rec loop e = iter {
  let! v = e
  printfn "%A" v
  yield v
  yield! loop e }
  
let e2 = iter { yield 1; yield 2; yield 3 }
let r = loop e2

r.MoveNext()
r.Current

// Implementing the zip function for enumerators
let rec zipE xs ys = iter {
  let! x = xs
  let! y = ys
  match x, y with 
  | Some(x), Some(y) ->
    yield x, y
    yield! zipE xs ys 
  | _ -> () }

// Implementing zip for sequences using zip for enumerators
let zip (a:seq<_>) (b:seq<_>) = 
  Enumerator.toSeq (fun () -> 
    zipE (a.GetEnumerator()) (b.GetEnumerator()))

zip [1;2;3] ["a";"b";"c"]
zip [1;2;3;4] ["a";"b";"c"]
Multiple items
val single : a:'a -> IEnumerator<'a>

Full name: Script.Enumerator.single

--------------------
type single = System.Single

Full name: Microsoft.FSharp.Core.single
val a : 'a
val state : int ref
Multiple items
val ref : value:'T -> 'T ref

Full name: Microsoft.FSharp.Core.Operators.ref

--------------------
type 'T ref = Ref<'T>

Full name: Microsoft.FSharp.Core.ref<_>
Multiple items
type IEnumerator =
  member Current : obj
  member MoveNext : unit -> bool
  member Reset : unit -> unit

Full name: System.Collections.IEnumerator

--------------------
type IEnumerator<'T> =
  member Current : 'T

Full name: System.Collections.Generic.IEnumerator<_>
val self : IEnumerator<'a>
property IEnumerator.Current: 'a
property Ref.Value: int
val invalidOp : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.invalidOp
namespace System
namespace System.Collections
type IEnumerator =
  member Current : obj
  member MoveNext : unit -> bool
  member Reset : unit -> unit

Full name: System.Collections.IEnumerator
val self : IEnumerator
property IEnumerator.Current: obj
val box : value:'T -> obj

Full name: Microsoft.FSharp.Core.Operators.box
IEnumerator.MoveNext() : bool
IEnumerator.Reset() : unit
type IDisposable =
  member Dispose : unit -> unit

Full name: System.IDisposable
val self : System.IDisposable
System.IDisposable.Dispose() : unit
val combine : a:IEnumerator<'a> -> b:IEnumerator<'a> -> IEnumerator<'a>

Full name: Script.Enumerator.combine
val a : IEnumerator<'a>
val b : IEnumerator<'a>
val current : IEnumerator<'a> ref
val first : bool ref
property Ref.Value: IEnumerator<'a>
property Ref.Value: bool
val zero : unit -> IEnumerator<'a>

Full name: Script.Enumerator.zero
val delay : f:(unit -> IEnumerator<'a>) -> IEnumerator<'a>

Full name: Script.Enumerator.delay
val f : (unit -> IEnumerator<'a>)
val en : Lazy<IEnumerator<'a>>
Multiple items
active recognizer Lazy: Lazy<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.( |Lazy| )

--------------------
type Lazy<'T> = System.Lazy<'T>

Full name: Microsoft.FSharp.Control.Lazy<_>
property System.Lazy.Value: IEnumerator<'a>
val toSeq : gen:(unit -> IEnumerator<'T>) -> IEnumerable<'T>

Full name: Script.Enumerator.toSeq
val gen : (unit -> IEnumerator<'T>)
Multiple items
type IEnumerable =
  member GetEnumerator : unit -> IEnumerator

Full name: System.Collections.IEnumerable

--------------------
type IEnumerable<'T> =
  member GetEnumerator : unit -> IEnumerator<'T>

Full name: System.Collections.Generic.IEnumerable<_>
val x : IEnumerable<'T>
IEnumerable.GetEnumerator() : IEnumerator<'T>
val x : IEnumerable
IEnumerable.GetEnumerator() : IEnumerator
Multiple items
type EnumerableBuilder =
  new : unit -> EnumerableBuilder
  member Bind : a:IEnumerator<'a> * f:('a option -> IEnumerator<'b>) -> IEnumerator<'b>
  member Combine : a:IEnumerator<'b> * b:IEnumerator<'b> -> IEnumerator<'b>
  member Delay : f:(unit -> IEnumerator<'e>) -> IEnumerator<'e>
  member Yield : a:'c -> IEnumerator<'c>
  member YieldFrom : a:'d -> 'd
  member Zero : unit -> IEnumerator<'a>

Full name: Script.EnumerableBuilder

--------------------
new : unit -> EnumerableBuilder
val x : EnumerableBuilder
member EnumerableBuilder.Delay : f:(unit -> IEnumerator<'e>) -> IEnumerator<'e>

Full name: Script.EnumerableBuilder.Delay
val f : (unit -> IEnumerator<'e>)
module Enumerator

from Script
member EnumerableBuilder.YieldFrom : a:'d -> 'd

Full name: Script.EnumerableBuilder.YieldFrom
val a : 'd
member EnumerableBuilder.Yield : a:'c -> IEnumerator<'c>

Full name: Script.EnumerableBuilder.Yield
val a : 'c
val single : a:'a -> IEnumerator<'a>

Full name: Script.Enumerator.single
member EnumerableBuilder.Bind : a:IEnumerator<'a> * f:('a option -> IEnumerator<'b>) -> IEnumerator<'b>

Full name: Script.EnumerableBuilder.Bind
val f : ('a option -> IEnumerator<'b>)
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
member EnumerableBuilder.Combine : a:IEnumerator<'b> * b:IEnumerator<'b> -> IEnumerator<'b>

Full name: Script.EnumerableBuilder.Combine
val a : IEnumerator<'b>
val b : IEnumerator<'b>
member EnumerableBuilder.Zero : unit -> IEnumerator<'a>

Full name: Script.EnumerableBuilder.Zero
val iter : EnumerableBuilder

Full name: Script.iter
val e : IEnumerator<int>

Full name: Script.e
property IEnumerator.Current: int
val loop : e:IEnumerator<'a> -> IEnumerator<'a option>

Full name: Script.loop
val e : IEnumerator<'a>
val v : 'a option
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val e2 : IEnumerator<int>

Full name: Script.e2
val r : IEnumerator<int option>

Full name: Script.r
property IEnumerator.Current: int option
val zipE : xs:IEnumerator<'a> -> ys:IEnumerator<'b> -> IEnumerator<'a * 'b>

Full name: Script.zipE
val xs : IEnumerator<'a>
val ys : IEnumerator<'b>
val x : 'a option
val y : 'b option
val x : 'a
val y : 'b
val zip : a:seq<'a> -> b:seq<'b> -> IEnumerable<'a * 'b>

Full name: Script.zip
val a : seq<'a>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Core.Operators.seq

--------------------
type seq<'T> = IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
val b : seq<'b>
IEnumerable.GetEnumerator() : IEnumerator<'a>
IEnumerable.GetEnumerator() : IEnumerator<'b>
Raw view Test code New version

More information

Link:http://fssnip.net/37
Posted:13 years ago
Author:Tomas Petricek
Tags: enumerator , seq , ienumerator , computation builder