开发者

Initializing an infinite list of BigIntegers

开发者 https://www.devze.com 2023-03-11 06:00 出处:网络
Ok, So I need a list of all the positive integers. What first comes to mind is: let numbers:Seq<bigint>=Seq.initInfinite n...

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
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号