Initializing an infinite list of BigIntegers
Ok, So I need a list of all the positive integers. What first comes to mind is:
let numbers:Seq<bigint>=Seq.initInfinite n...
but initInfite isn't actually infitint: http://msdn.microsoft.com/en-us/library/ee370429.aspx (unlike bigint) its only: Int32.MaxValue = 2,147,483,647 which is nowhere near big e开发者_运维问答nough.
Currently my plan is to replace the sequence with some kind of handmade class (possibly implimenting IEnumerable). It would be simple (and possibly more effiecint for my use) but i want to know how to do this
Seq.unfold (fun n -> Some(n, n + 1I)) 0I
let numbers:bigint seq =
let rec loop n = seq { yield n; yield! loop (n+1I) }
loop 0I
I keep the following statically constrained function around since it is very flexible (you can specify the start value and the skip interval) and works with all numeric types:
let inline infiniteRange start skip =
seq {
let n = ref start
while true do
yield n.contents
n.contents <- n.contents + skip
}
Type signature given by FSI:
val inline infiniteRange :
^a -> ^b -> seq< ^a>
when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^a)
Here is how you generate all positive integers (BigInts, that is -- shown in FSI):
> infiniteRange 1I 1I;;
val it : seq<System.Numerics.BigInteger> =
seq [1 {IsEven = false;
IsOne = true;
IsPowerOfTwo = true;
IsZero = false;
Sign = 1;}; 2 {IsEven = true;
IsOne = false;
IsPowerOfTwo = true;
IsZero = false;
Sign = 1;}; 3 {IsEven = false;
IsOne = false;
IsPowerOfTwo = false;
IsZero = false;
Sign = 1;}; 4 {IsEven = true;
IsOne = false;
IsPowerOfTwo = true;
IsZero = false;
Sign = 1;}; ...]
Update: and as Daniel showed, you can use generic language primitives to easily write another statically constrained function in terms infiniteRange
with skip 1 built-in:
let inline infiniteRangeSkip1 start =
infiniteRange start LanguagePrimitives.GenericOne
Here's the type signature:
val inline infiniteRangeSkip1 :
^a -> seq< ^a>
when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^a) and
^b : (static member get_One : -> ^b)
You might even consider extending the Seq
module if this is something you frequently need.
module Seq =
let initInfiniteBig =
seq {
let i = ref 0I
while true do
yield !i
i := !i + 1I
}
let ten = Seq.initInfiniteBig |> Seq.take 10
Update
I benchmarked a few variations:
let initInfiniteBig =
seq {
let i = ref 0I
while true do
yield !i
i := !i + 1I
}
let initInfiniteBig2 =
seq {
let i = ref 0I
while true do
yield i.contents
i.contents <- i.contents + 1I
}
let initInfiniteBig3 =
let rec loop i =
seq {
yield i
yield! loop (i + 1I)
}
loop 0I
let initInfiniteBig4 = Seq.unfold (fun n -> Some(n, n + 1I)) 0I
let range s = s |> Seq.take 100000000 |> Seq.length |> ignore
range initInfiniteBig //Real: 00:00:29.913, CPU: 00:00:29.905, GC gen0: 0, gen1: 0, gen2: 0
range initInfiniteBig2 //Real: 00:00:30.045, CPU: 00:00:30.045, GC gen0: 0, gen1: 0, gen2: 0
range initInfiniteBig3 //Real: 00:00:40.345, CPU: 00:00:40.310, GC gen0: 2289, gen1: 5, gen2: 0
range initInfiniteBig4 //Real: 00:00:30.731, CPU: 00:00:30.716, GC gen0: 1146, gen1: 4, gen2: 1
Update 2
Here's a generic range function, like Stephen's, but without start
and skip
.
let inline infiniteRange() : seq<'a> =
let zero : 'a = LanguagePrimitives.GenericZero
let one : 'a = LanguagePrimitives.GenericOne
seq {
let n = ref zero
while true do
yield !n
n := !n + one
}
Here's the signature:
unit -> seq< ^a>
when ^a : (static member get_Zero : -> ^a) and
^a : (static member get_One : -> ^a) and
^a : (static member ( + ) : ^a * ^a -> ^a)
And the benchmark:
range (infiniteRange() : seq<bigint>) //Real: 00:00:30.042, CPU: 00:00:29.952, GC gen0: 0, gen1: 0, gen2: 0
精彩评论