📘 ‘Guess the number’ in Perl 6

Write a program that generates a random integer number 0 through 10 and asks the user to guess it, saying if the entered value is too small or too big.

First, a random number needs to be generated. In Perl 6, the randroutine can be called on an integer object, and it returns a random floating-point value between 0 and that integer. As the task requires a random integer number, call the roundmethod on the result:

10.rand.round

Now, ask for the initial guess and enter the loop, which compares the guess with $n.

my $n = 10.rand.round;

my $guess = prompt('Guess my number between 0 and 10: ');

while $guess != $n {
    if $guess < $n {
        say 'Too small.';
    }
    elsif $guess > $n {
        say 'Too big.';
    }
    $guess = prompt('Try again: ');
}

say 'Yes, this is it!';

The ifelsif chain may be replaced with a ternary operator:

say $guess < $n ?? 'Too small.' !! 'Too big.';

📘 All Unicode digits in Perl 6

Print all Unicode digits.

Perl 6 has the best support of Unicode among the modern programming languages. When talking about digits, it is worth remembering that the Unicode standard marks as digits much more than the regular ten characters used in English, for example.

Let us iterate over the whole range of codepoints and select the digits using the <:digit> character class in the regex.

for 1 .. 0x10FFFD {
    my $char = $_.chr;
    print $char if $char ~~ /<:digit>/;
}

The program prints 580 characters. Let us list some of them:

0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९০১২৩৪৫৬৭৮৯੦੧੨੩੪੫੬੭੮੯૦૧૨૩૪૫૬૭૮૯୦୧୨୩୪୫୬୭୮୯௦௧௨௩௪௫௬௭௮௯౦౧౨౩౪౫౬౭౮౯೦೧೨೩೪೫೬೭೮೯൦൧൨൩൪൫൬൭൮൯๐๑๒๓๔๕๖๗๘๙໐໑໒໓໔໕໖໗໘໙༠༡༢༣༤༥༦༧༨༩၀၁၂၃၄၅၆၇၈၉០១២៣៤៥៦៧៨៩᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙0123456789𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿

If you want to know the name of the Unicode digit, call the uniname method on the character:

say $char ~ ' ' ~ $char.uniname if $char ~~ /<:digit>/;

For example, Arabic digits 0 to 9 have simple names such as DIGIT ZERO; another set of Arabic digits ٠,١, ٢, etc., up to ٩ has names such as ARABIC-INDIC DIGIT THREE.

📘 Implementing the Monte Carlo method using Perl 6

Calculate the area of a circle and the volume of a sphere of radius 1 using the Monte Carlo method.

The Monte Carlo method is a statistical method of calculating data whose formula is not known. The idea is to generate a big number of random numbers and see how many of them satisfy the condition.

To calculate the area of a circle of the radius 1, pairs of random numbers between 1and 1 are generated. These pairs represent the points in the square in the center of coordinates with sides of length 2. The area of the square is thus 4. If the distance between the random point and the center of the square is less than 1, then this point is located inside the circle of that radius. Counting the number of points that landed inside the circle and the number of points outside the circle gives the approximate value of the area of the circle, as soon as the area of the square is known. Here is the program.

my $inside = 0;
my $n = 100_000;
for 1..$n {
    my @point = map {2.rand - 1}, 1..2; # 1..3 for sphere
    $inside++ if sqrt([+] map *², @point) < 1;
}
say 4 * $inside / $n;                   # 8 for sphere

The bigger the number of repetitions $n, the more accurate is the result. In one of the runs of the program, it printed 3.14392, which is close to the true result, which is πr², which is equal to π in our case. We see that the Monte Carlo result is very close. For the volume of a sphere, change the program according to the comments. The formula is 4/3 πr³, which approximately gives 4.189.

📘 Working with polar coordinates in Perl 6

Convert the Cartesian coordinates to polar and backward.

Polar coordinates are a convenient way of representing points on a surface with the two values: distance from the centre of coordinates and the angle between the vector and the pole axis.

The conversion formulae between the Cartesian and polar systems,which is valid for positive x and y, are the following:

These expressions can be implemented as-is in the Perl 6 code:

sub polar-to-cartesian($r, $φ) {
    $r * cos($φ), $r * sin($φ)
}

sub cartesian-to-polar($x, $y) {
    sqrt($x² + $y²), atan($y / $x)
}

The functions return lists of either polar or Cartesian coordinates. Because of the simplicity of implementation, it is fine to omit the returnkeyword and the semicolon at the end of the line.

Call the conversion functions with some positive numbers and check that the initial coordinates are restored after the second conversion:

say cartesian-to-polar(1, 2);
say polar-to-cartesian(2.236068, 1.107149);

For the negative x and y, the Cartesian-to-polar conversion is a bit more complicated. Depending on the quadrant of the point, the φ  value is bigger or smaller by . When xis zero, it is either –pi/2 or pi/2.

All these variants can be implemented in Perl 6 by using multi-subroutines with the whereclause, as demonstrated below:

sub cartesian-to-polar($x, $y) {
    sqrt($x² + $y²), cartesian-to-φ($x, $y)
}

multi sub cartesian-to-φ($x, $y where {$x > 0}) {
    atan($y / $x) 
}

multi sub cartesian-to-φ($x, $y where {$x < 0 && $y ≥ 0}) {
    atan($y / $x) + π
}

multi sub cartesian-to-φ($x, $y where {$x < 0 && $y < 0}) {
    atan($y / $x) – π
}

multi sub cartesian-to-φ($x, $y where {$x == 0 && $y > 0}) {
    π / 2
}

multi sub cartesian-to-φ($x, $y where {$x == 0 && $y < 0}) {
    -π / 2
}

multi sub cartesian-to-φ($x, $y where {$x == 0 && $y == 0}) {
    Nil
}

📘 Computing standard deviation using Perl 6

For the given data, calculate the standard deviation value (sigma).

Standard deviation is a statistical term that shows how compact data distribution is. The formula is the following:

where N is the number of elements in the array x; is the average value (see Task 56, Average on an array).

Let’s use some test data from Wikipedia and take the straightforward approach using reduction operations and avoiding explicit loops:

my @data = 727.7, 1086.5, 1091.0, 1361.3, 1490.5, 1956.1;

my $avg = ([+] @data) / @data.elems;
my $sigma = sqrt(
    ([+] map * ** 2, map * - $avg, @data) /
    (@data.elems - 1)
);

say $sigma; # 420.962489619523

Inside the sqrt function, the [+] reduction operator gets the array that is formed by the two nested runs of map. First, the constant shift has been removed by applying * - $avg to each element. Second, a square of each item has been calculated: * ** 2.

In both cases, the WhateverCode is used. It is usually more expressive but may lead to constructs like * ** 2, which look a bit cryptic.

The two maps can be merged into one:

my $sigma = sqrt(
    ([+] map (* - $avg) ** 2, @data)  / (@data.elems - 1)
);

Now, let’s explore the second approach that gets the same result using feed operators. In Perl 6, there are feed operators of both directions: <== and  ==>. Their shape indicates the direction of data flow, so here is another version of the program.

my @data = 727.7, 1086.5, 1091.0, 1361.3, 1490.5, 1956.1;

my $avg = ([+] @data) / @data.elems;
@data
    ==> map * - $avg
    ==> map * ** 2
    ==> reduce * + *
    ==> my @σ;
say sqrt(@σ[0] / (@data.elems - 1)); # 420.962489619523

The data flow is clearly visible now. The @data array passes the two maps, and then, it is reduced using the + operation. The call of reduce * + * is equivalent to using the reduction operator in the form of [+].

Notice how the  array is defined, not only the fact that a Unicode name is used but mostly the fact that the my declaration is placed at the end of the feed chain. An array is used here because the feed operator does not return a scalar value, although we only need one element.

To make the code even closer to the original mathematical formula, you may choose a different name for the variable holding the average value (and remove the elemscall):

my $x̄= ([+] @data) / @data;

📘 Computing the distance between two points using Perl 6

Calculate the distance between the two points on a surface.

There are two points on a surface, each with their own coordinates, xand y. The task is to find the distance between these two points.

A straightforward solution would be to use the Pythagorean theorem:

my ($x1, $y1) = (10, 3);
my ($x2, $y2) = (9, 1);
say sqrt(($x1 - $x2) ** 2 + ($y1 - $y2) ** 2);

This works, but it requires a lot of typing. In Perl 6, there is a slightly easier way if you use complex numbers.

my $a = 10+3i;
my $b = 9+1i;
say ($a - $b).abs;

The result of both programs is the same. Complex numbers are objects of the Complex data type and are introduced via i—the imaginary unit:

The result of the difference $a - $b is also a complex number, and the abs method can be called on it. This method returns the absolute value of a complex number, which is actually the distance between the two points.

Notice the style: 10+3i vs. 10 + 3i. The first one seems to be preferable as it is also used as the default output format by the compiler. The second option may be confusing when a complex number is used in an expression with other variables or numbers.

$ perl6 -e'say (10+3i, -i, 4i, 10+0i)'
(10+3i -0-1i 0+4i 10+0i)

📘 Generating a histogram of random numbers using Perl 6

Test the quality of the random generator by using a histogram to visualise the distribution.

The quality of the built-in generator of random numbers fully depends on the algorithm the developers of the compiler used. As a user, you cannot do much to change the existing generator, but you can always test if it delivers numbers uniformly distributed across the whole interval.

In Perl 6, there is the rand routine (see Task 32, Generating random numbers) that returns a floating-point number (actually, the value of the Num type) between 0 and 1. We will run it 100,000 times, filling the histogram containing 10 cells. Each random number falls into one of them. For example, the numbers between 0 and 0.1 land in the first cell, the numbers between 0.1 and 0.2 in the second, and so on.

my @histogram;
@histogram[10 * rand]++ for 1..100_000;
say @histogram;

Examine the way the index for the @histogram array is formed. A random integer between 0 and 1 is first multiplied by 10 and then an integer part of it is taken because the array indexing operator [ ] needs integers only. It is also possible to do the conversion explicitly:

@histogram[(10 * rand).Int]++

Run the program a few times. Here’s the output of a couple of runs of the program, and it printed more or less equal numbers in each cell:

[10062 9818 10057 9922 10002 10118 9978 9959 10013 10071]
[9959 9957 9813 9933 10160 10030 10036 10032 10059 10021]