Specify the number of decimal digits on the explicit generator of a sequence in Raku

151 Views Asked by At

I've written a simple code:

sub euler-ex ($x) of Seq {
   1, { $x**++$ / [×] 1 .. ++$ } ... Inf }
say " 5: " ~ euler-ex(5)[^20]  ~ " = " ~ [+](euler-ex(5)[^20]);

The output:

5: 1 5 12.5 20.833333 26.041667 26.041667 21.701389 15.500992 9.68812 5.382289 2.6911445 1.22324748 0.50968645 0.19603325 0.07001187499 0.023337291662 0.0072929036444 0.00214497166011 0.000595825461143 0.000156796173985 = 148.41310786833832

How to specify the number of digits on that output, i.e. on the decimals coming out of the explicit generator of the sequence?

2

There are 2 best solutions below

1
Coke On BEST ANSWER

You can change the last line to

say " 5: " ~ euler-ex(5)[^20].fmt("%0.10f")  ~ "=" ~ [+](euler-ex(5)[^20]).fmt("%0.10f");

and get output like

 5: 1.0000000000 5.0000000000 12.5000000000 20.8333333333 26.0416666667 26.0416666667 21.7013888889 15.5009920635 9.6881200397 5.3822889109 2.6911444555 1.2232474798 0.5096864499 0.1960332500 0.0700118750 0.0233372917 0.0072929036 0.0021449717 0.0005958255 0.0001567962=148.4131078683

You might be interested to know that this sequence is generating Rats, so you could get the exact value if you want. You can see the "raw" values with

dd euler-ex(5)[^20];

(1, 5.0, 12.5, <125/6>, <625/24>, <625/24>, <3125/144>, <15625/1008>, <78125/8064>, <390625/72576>, <390625/145152>, <1953125/1596672>, <9765625/19160064>, <48828125/249080832>, <244140625/3487131648>, <244140625/10461394944>, <1220703125/167382319104>, <6103515625/2845499424768>, <30517578125/51218989645824>, <152587890625/973160803270656>)
0
Mustafa Aydın On

You can round a number to wanted scale:

>>> 12.8471 .round(0.01)
12.85

12.8471 is rounded to the nearest multiple of 0.01; effectively, 2 decimal places.

You have a sequence of numbers, so map is there:

>>> (1, * +  ... *) .head(5)
(1 3.718281828459045 6.43656365691809 9.154845485377136 11.87312731383618)

>>> (1, * +  ... *) .map(*.round(0.01)) .head(5)
(1 3.72 6.44 9.15 11.87)

So for, e.g., 2 decimal points, need 0.01 == 10 ** -2; then we can argumentize this to the function:

sub euler-ex($x, Int :$digits) of Seq {
   my $seq := 1, { $x ** ++$ / [×] 1 .. ++$ } ... Inf;
   $digits
       ?? $seq.map(*.round(10 ** -$digits))
       !! $seq
}

>>> euler-ex(5).head(6)
(1 5 12.5 20.833333 26.041667 26.041667)

>>> euler-ex(5, :3digits).head(6)
(1 5 12.5 20.833 26.042 26.042)

If $digits is supplied (and nonzero...), the sequence will be returned back with its computed values rounded to that many decimal places. Otherwise, as is.


Noting that round gives back a numeric value (e.g., Rational above), and 12.3. round(0.01) would give 12.3 not 12.30, i.e., trailing zeros are not put. If that is wanted, .fmt as the other answer (but in map; or sprintf) is possible (noting that they give strings).