📘 Converting text to Morse code using Perl 6

Convert the given text to the Morse code.

Converting text to the Morse code is a relatively easy task. The solution is to replace all the alphanumeric characters with the corresponding representation in the Morse code.

In this solution, all the other characters are ignored and are removed from the source string. In the Morse code, letters are separated by the duration of one dash, and words are separated by the duration of approximately 2.5 dashes, so in the program, one space is used for separating characters, and three spaces separate the words.

The above logic is programmed in the series of replacements. First, lowercase the whole phrase (there is no distinction between lower- and upper-case letters) and then remove all the non-alphanumeric characters and increase the distance between the words. Finally, replace each remaining printable symbol with the corresponding Morse sequence.

my %code = (
    a => '.-',      b => '-...',    c => '-.-.',
    d => '-..',     e => '.',       f => '..-.',
    g => '--.',     h => '....',    i => '..',
    j => '.---',    k => '-.-',     l => '.-..',
    m => '--',      n => '-.',      o => '---',
    p => '.--.',    q => '--.-',    r => '.-.',
    s => '...',     t => '-',       u => '..-',
    v => '...-',    w => '.--',     x => '-..-',
    y => '-.--',    z => '--..',    0 => '-----', 
    1 => '.----',   2 => '..---',   3 => '...--',
    4 => '....-',   5 => '.....',   6 => '-....',
    7 => '--...',   8 => '---..',   9 => '----.'
);

my $phrase = prompt('Your phrase in plain text> ');

$phrase.=lc;
$phrase ~~ s:g/<-[a..z0..9]>/ /;
$phrase ~~ s:g/\s+/ /;
$phrase ~~ s:g/(<[a..z0..9]>)/%code{$0} /;

say $phrase; 

Let us test this on a random phrase:

$ perl6 morse.pl 
Your phrase in plain text> Hello, World!
.... . .-.. .-.. ---  .-- --- .-. .-.. -..  

The conversion table takes the biggest part of the program.

The regexes show how character classes are created in Perl 6.

A characters class with a range of symbols:

<[a..z0..9]>

A negative character class, which matches with any character other than the one from the range:

<-[a..z0..9]>

These character classes list all the allowed characters that can be encoded by the given %code hash. It is also possible to use \w and \W or <alnum> and <!alnum> instead of the above regexes if you are sure that the input string is pure ASCII. All the regexes in the program come with the :g adverb to make them global. Regex matching uses the double tilde ~~ operator for both matching and replacement.

📘 Reading directory content using Perl 6

Print the file names from the current directory.

Reading a directory in Perl 6 can be done using the dir routine defined in the IO::Path class.

say dir();

This tiny program does not do the task really satisfactory, as the dir routine returns a lazy sequence (an object of the Seq data type) of IO::Path objects.

To get the textual file names, take the path part of an IO::Path object using the path method:

.path.say for dir;

The code is equivalent to the more verbose fragment:

for dir() -> $file {
    say $file.path;
}

If you want to print full paths of the files in a directory, use the absolute method:

.absolute.say for dir;

The test named argument of the dir routine allows selecting filenames that match a certain regex, for example, listing all jpeg files:

for dir(test => /\.jpg$/) -> $file {
    say $file.path;
}

📘 The uniq utility written in Perl 6

Create the simple equivalent of the UNIX uniqutility, which only prints the lines from the STDIN input, which are not repeating the previous line.

The solution of this task can be built on the solution of Task 95, The catutility. This time, the entered lines have to be saved, so let’s introduce the $previous variable and make an explicit code block for the loop.

my $previous = '';
while (my $line = $*IN.get) {
    say $line unless $line eq $previous;
    $previous = $line;
}

On each iteration, the next line from the $*IN handle is read and saved in the $line variable. If the value is different from the previous line, which is saved in the $previous variable, then the current line has been printed.

At the moment, only duplicated lines are affected. If the two identical lines are separated by other lines, then everything is printed. Let us modify the program so that it only prints the unique lines per whole input stream.

my %seen;
while (my $line = $*IN.get) {
    next if %seen{$line};
    say $line;
    %seen{$line} = 1;
}

Here, the %seen hash is used as a storage of the lines printed. It’s also a good practice to use a Set object instead; see the example of using a set in Task 54, Exclusion of two arrays.

📘 The cat utility written in Perl 6

Create the equivalent of the UNIX catutility that copies its STDIN input directly to STDOUT.

Reading from the input and sending it to the output is a relatively easy task. The $*IN handle is by default connected to STDIN. Being an object of the IO::Handle type, it has the slurp method that returns the whole input text in one go. What is left to do, is just to print it to the output, which defaults to STDOUT.

Here’s the complete program:

say $*IN.slurp;

This works well, and we can use it via the command line:

$ perl6 cat.pl < file1.txt > file2.txt

However, in the interactive mode, the program does not replicate each entered line, but instead, it waits until the end of the output (say, until you press Ctrl+D). This behaviour is fully explainable because of the use of the slurpmethod.

Let us modify the program so that it prints each line as soon as it is entered. The IO::Handle class has another method, get, which reads one line from the handle and returns it. Create a loop and print the line after it is delivered by the get method:

.say while $_ = $*IN.get;

Here, the default variable $_ is used. This allows to omit creating the new variable and to make the whole program more compact. The .say call in it is a shortcut for $_.say.

📘 Parallel file processing in Perl 6

Process the files from the current directory in a few parallel threads.

We have to do something with each file in the directory, and it has to be done in such a way that files are processed independently with a few workers. It is not possible to predict how long the process will take for each individual file, that’s why we need a common queue, which supplies the filenames for the next available worker.

A good candidate for the queue is a channel.

my $channel = Channel.new();
$channel.send($_) for dir();
$channel.close;

All the file names are sent to the channel, which we close afterward. (On how to read directories, see more details in Task 97, Reading directory contents.)

Channels are designed to work thread-safe. It means that it is possible to get data from the channel using several threads, and each value is processed only once. Perl 6 cannot predict which thread gets which name but it can guarantee that each data item is only read by the threads once.

my @workers;
for 1..4 {
    push @workers, start {
        while (my $file = $channel.poll) {
            do_something($file);
        }
    } 
}

The code on the previous page creates four independent workers using the startkeyword. As they are executed independently not only from each other but also from the main program, it is important to wait until all of them are done:

await(@workers);

The elements of the @workers array are promises (objects of the Promise data type). The await routine waits until all the promises are kept.

Another practical way of creating and waiting workers is shown in Task 92, Sleep Sort: instead of collecting them in an array, you can use the gather and take keywords.

Examine the main loop:

while (my $file = $channel.poll) {
    do_something($file);
}

On each iteration, a value from the channel is read. The poll method ensures that the reading stops after the channel is exhausted.

All four threads are doing similar work and are polling the same channel. This approach distributes the filenames that were sent to the channel between the workers. As a name has been read, it is removed from the channel, and the next read request returns the next name.

Finally, cook the do_something sub according to your needs. In the following simplest example, it only prints filenames:

sub do_something($file) {
    say $file.path;
}

📘 Atomic operations in Perl 6

Using the solution for Task 38, the Monte Carlo method, create a program that calculates the result using multiple threads.

Perl 6 has built-in support for parallel computing. In Task 92, Sleep Sort, we’ve seen how to use the keywords awaitgather, and take to spawn a few threads and wait for them to finish. 

When different threads want to modify the same variable, such as a counter, it is wise to introduce atomic operations to make sure the threads do not interfere with each other. Here is the modification of the Monte Carlo program calculating the area of a circle with four parallel threads.

my atomicint $inside = 0;

my $n = 5000;
my $p = 4;

await gather for 1..$p {
    take start {
        for 1..$n {
            my @point = map {2.rand - 1}, 1..2;
            $inside⚛++ if sqrt([+] map *², @point) <= 1;
        }
    }
}

say 4 * $inside / $p / $n;

Run the program a few times, changing the value of $n (the number of random points per thread) and $p (the number of threads). The program should print the value that is close to pi, such as 3.141524.

The new thing here is the atomic increment operation:

$inside⚛++

An atomic operation ensures that the variable is modified with no conflicts between the threads.

The variable itself should be a native integer of a special type—notice how it is declared:

my atomicint $inside;

As the atomic operation uses the Unicode character, there is an ASCII alternative:

atomic-fetch-inc($inside)

Here’s a list of other atomic operations and their synonyms that can be used with parallel processes:

$var ⚛= $value       atomic-assign($var, $value)
my $a = ⚛$var         my $a = atomic-fetch($var)

$var⚛++               atomic-fetch-inc($var)
$var⚛--               atomic-fetch-dec($var)
++⚛$var               atomic-inc-fetch($var)
--⚛$var               atomic-dec-fetch($var)

$var ⚛+= $value       atomic-fetch-add($var, $value)
$var ⚛-= $value       atomic-fetch-dec($var, $value)

N. B. The code in this task works with the Rakudo Perl 6 compiler starting from version 2017.09. Earlier versions do not support atomic operators.

📘 Sleep Sort in Perl 6

Implement the Sleep Sort algorithm for a few small positive integer values.

The Sleep Sortis a funny implementation of the sorting algorithm. For each input value, an asynchronous thread starts, which waits for the number of seconds equals to the input number and then prints it. So, if all the threads are spawned simultaneously, the output of the program contains the sorted list.

Here is the solution in Perl 6. On the next page, we will go through the bits of it and explain all the important moments.

await gather for @*ARGS -> $value {
    take start {
        sleep $value/10;
        say $value;
    }
}

Pass the values via the command line, and get them sorted.

$ perl6 sleep-sort.pl 9 10 2 8 5 7 6 4 1 3
1
2
3
...
8
9
10

The input values from the command line come to the @*ARGS array. The first step is to iterate over the array:

for @*ARGS -> $value {    
     . . .
}

For each $value, the startblock creates a promise with a code block that waits for the time that is proportional to the value and prints the value after that time.

start {
    sleep $value/10;
    say $value;
}

Dividing the value by ten speeds up the program. On the other hand, the delay should not be too small to avoid race conditions between different threads.

After a separate promise has been created for each input number, the program has to wait until all of them are kept. To achieve that, the gathertake construction is used. The take keyword adds another promise to a sequence, which is then returned as a whole by the gather keyword.

gather for @*ARGS -> $value {
    take start {
        . . .
    }
}

Finally, the await routine ensures the program does not quit until all the promises are kept or, in other words, until all the numbers are printed.

await gather . . . {
    take start {
        . . .
    }
}