Skip to content

Commit 709ce85

Browse files
committed
Provided an optimized GetEnumerator for scan
1 parent 513ce76 commit 709ce85

File tree

3 files changed

+90
-11
lines changed

3 files changed

+90
-11
lines changed

src/fsharp/FSharp.Core/iseq.fs

+86-10
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,20 @@ namespace Microsoft.FSharp.Collections
108108
member __.GetHashCode o = c.GetHashCode o._1
109109
member __.Equals (lhs,rhs) = c.Equals (lhs._1, rhs._1) }
110110

111+
[<AbstractClass>]
112+
type PreferGetEnumerator<'T>() =
113+
inherit EnumerableBase<'T>()
114+
115+
abstract GetEnumerator: unit -> IEnumerator<'T>
116+
abstract GetSeq : unit -> ISeq<'T>
117+
118+
interface IEnumerable<'T> with
119+
member this.GetEnumerator () : IEnumerator<'T> = this.GetEnumerator ()
120+
121+
interface ISeq<'T> with
122+
member this.PushTransform<'U> (next:ITransformFactory<'T,'U>) : ISeq<'U> = (this.GetSeq()).PushTransform next
123+
member this.Fold<'Result> (f:PipeIdx->Folder<'T,'Result>) : 'Result = (this.GetSeq()).Fold f
124+
111125
[<CompiledName "Empty">]
112126
let empty<'T> = Microsoft.FSharp.Collections.SeqComposition.Core.EmptyEnumerable<'T>.Instance
113127

@@ -592,17 +606,79 @@ namespace Microsoft.FSharp.Collections
592606
let concat (sources:ISeq<#ISeq<'T>>) : ISeq<'T> =
593607
upcast (ThinConcatEnumerable (sources, id))
594608

609+
(*
610+
Represents the following seq comprehension, but they don't work at this level
611+
612+
seq {
613+
let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt folder
614+
let mutable state = initialState
615+
yield state
616+
for item in enumerable do
617+
state <- f.Invoke (state, item)
618+
yield state }
619+
*)
620+
type ScanEnumerator<'T,'State>(folder:'State->'T->'State, initialState:'State, enumerable:seq<'T>) =
621+
let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt folder
622+
623+
let mutable state = 0 (*Pre-start*)
624+
let mutable enumerator = Unchecked.defaultof<IEnumerator<'T>>
625+
let mutable current = initialState
626+
627+
interface IEnumerator<'State> with
628+
member this.Current: 'State =
629+
match state with
630+
| 0(*PreStart*) -> notStarted()
631+
| 1(*GetEnumerator*) -> current
632+
| 2(*MoveNext*) -> current
633+
| _(*Finished*) -> alreadyFinished()
634+
635+
interface IEnumerator with
636+
member this.Current : obj =
637+
box (this:>IEnumerator<'State>).Current
638+
639+
member this.MoveNext () : bool =
640+
match state with
641+
| 0(*PreStart*) ->
642+
state <- 1(*GetEnumerator*)
643+
true
644+
| 1(*GetEnumerator*) ->
645+
enumerator <- enumerable.GetEnumerator ()
646+
state <- 2(*MoveNext*)
647+
(this:>IEnumerator).MoveNext ()
648+
| 2(*MoveNext*) ->
649+
if enumerator.MoveNext () then
650+
current <- f.Invoke (current, enumerator.Current)
651+
true
652+
else
653+
current <- Unchecked.defaultof<_>
654+
state <- 3(*Finished*)
655+
false
656+
| _(*Finished*) -> alreadyFinished()
657+
658+
member this.Reset () : unit = noReset ()
659+
660+
interface IDisposable with
661+
member this.Dispose(): unit =
662+
if isNotNull enumerator then
663+
enumerator.Dispose ()
664+
595665
[<CompiledName "Scan">]
596-
let inline scan (folder:'State->'T->'State) (initialState:'State) (source:ISeq<'T>) :ISeq<'State> =
597-
let head = singleton initialState
598-
let tail =
599-
source.PushTransform { new ITransformFactory<'T,'State> with
600-
override __.Compose _ _ next =
601-
upcast { new Transform<'T,'V,'State>(next, initialState) with
602-
override this.ProcessNext (input:'T) : bool =
603-
this.State <- folder this.State input
604-
TailCall.avoid (next.ProcessNext this.State) } }
605-
concat (ofList [ head ; tail ])
666+
let scan (folder:'State->'T->'State) (initialState:'State) (source:ISeq<'T>) : ISeq<'State> =
667+
upcast { new PreferGetEnumerator<'State>() with
668+
member this.GetEnumerator () =
669+
upcast new ScanEnumerator<'T,'State>(folder, initialState, source)
670+
671+
member this.GetSeq () =
672+
let head = singleton initialState
673+
let tail =
674+
source.PushTransform { new ITransformFactory<'T,'State> with
675+
override __.Compose _ _ next =
676+
let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt folder
677+
upcast { new Transform<'T,'V,'State>(next, initialState) with
678+
override this.ProcessNext (input:'T) : bool =
679+
this.State <- f.Invoke (this.State, input)
680+
TailCall.avoid (next.ProcessNext this.State) } }
681+
concat (ofList [ head ; tail ]) }
606682

607683
[<CompiledName "Skip">]
608684
let skip (skipCount:int) (source:ISeq<'T>) : ISeq<'T> =

src/fsharp/FSharp.Core/iseq.fsi

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ namespace Microsoft.FSharp.Collections
217217
val inline reduce : f:('T->'T->'T) -> source:ISeq<'T> -> 'T
218218

219219
[<CompiledName "Scan">]
220-
val inline scan : folder:('State->'T->'State) -> initialState:'State -> source:ISeq<'T> -> ISeq<'State>
220+
val scan : folder:('State->'T->'State) -> initialState:'State -> source:ISeq<'T> -> ISeq<'State>
221221

222222
[<CompiledName "Skip">]
223223
val skip : skipCount:int -> source:ISeq<'T> -> ISeq<'T>

src/fsharp/FSharp.Core/seqcore.fsi

+3
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ namespace Microsoft.FSharp.Collections.SeqComposition
7474
abstract member Append : ISeq<'T> -> ISeq<'T>
7575
abstract member Length : unit -> int
7676
abstract member GetRaw : unit -> seq<'T>
77+
default Append : ISeq<'T> -> ISeq<'T>
78+
default Length : unit -> int
79+
default GetRaw : unit -> seq<'T>
7780
interface ISeq<'T>
7881

7982
[<AbstractClass>]

0 commit comments

Comments
 (0)