📘 String length in Perl 6

Print the length of a string.

Perl 6 handles all strings as UTF-8 by default. This is why there is more than one parameter describing the length of the string. In fact, the lengthroutine does not exist, and an attempt to use it issues an error message with some hints to which other methods you can use.

To get the length of the string in the sense of number of characters, use the charsmethod:

say 'hello'.chars;  # 5
say 'café'.chars;   # 4
say 'привет'.chars; # 6

The results reflect the intuitive expectation and do not depend on actual representation of the characters. The first string fits in the ASCII table, the second may still be encoded in an 8-bit encoding Latin-1, and the third needs two bytes per character in the UTF-8 encoding. 

Another method, codes, returns the number of codepoints in the Unicode space. For the above examples, both charsand codesreturn the same numbers, as all the characters can be represented by a single codepoint.

say 'hello'.codes;  # 5
say 'café'.codes;   # 4
say 'привет'.codes; # 6

Although, when using combining characters, you may create a character that does not exist as a separate character in the Unicode table. In this case, the results of charsand codesmay differ.

Consider an example with a character built out of two elements: Latin letter xand a combining character COMBINING OGONEK. Together, they form a non-existing letter, which is one character, but two codepoints:

say 'x'­­.chars; # 1
say 'x'.codes; # 2

Let us dig a bit into how the above character is represented in the UTF-8 encoding. It consists of two parts: LATIN SMALL LETTER X and the combining character COMBINING OGONEK. The letter itself is a one-byte code 0x78, and the combining character has the Unicode entry point 0x328 and needs two bytes in UTF-8: 0xCC 0xA8.

Let us rewrite the example by explicitly specifying the codepoint of the combining character:

say "x\x[0328]".chars; # 1
say "x\x[0328]".codes; # 2

The above example was about the character that does not exist in Unicode as a single codepoint. Now, let us use another letter, saye, which forms an existing character with the same combining character: ę.

say 'ę'.chars; # 1
say 'ę'.codes; # 1

In this case, both charsand codesmethods return 1. Even if the string is built using an explicit combining character, the codesmethod coerces it back to the proper codepoint and does not count it as a separate code:

say "e\x[0328]".chars; # 1
say "e\x[0328]".codes; # 1

Thus, in many cases, to get the length of a string, it is enough to use the charsmethod called on that string.

📘 Greet a person using Perl 6

Ask a user for their name and greet them by printing ‘Hello, <Name>!’

Perl 6 offers a simple promptfunction that performs both actions: prints a prompt and reads the input. So, the program using it may look like this:

say 'Hello, ' ~ prompt('Enter your name: ') ~ '!';

The ~operator stands for string concatenation in Perl 6. Don’t be confused by the sequence of text strings in this code. To build the string, Perl needs to have all its parts. Two of them ('Hello',and '!') are presented by literal strings, while the middle part needs user input. Therefore, the flow of the whole program remains logical:

Enter your name: Andy
Hello, Andy!

If you prefer a more traditional program flow, split it into separate parts and interpolate a variable in a string:

my $name = prompt('Enter your name: ');
say "Hello, $name!";

Alternatively, the get function may be used. It returns the input line without the newline character. Printing a prompt message is your responsibility now:

print 'Enter your name: ';
my $name = get();
say "Hello, $name!";

The get function may be called as a method on the $*IN variable, which is by default connected to the standard input:

my $name = $*IN.get();

📘 Hello, World! in Perl 6

Print ‘Hello, World!’

There are two built-in functions in Perl 6 to print to the console: printand say. Both print their arguments, but the say routine additionally ends the output with a newline character.

So, the quickest solution is to use sayand pass a string with no newlines:

say 'Hello, World!'

Another solution is to use print and include the \n character in the string itself:

print "Hello, World!\n"

The output of either program is the same:

Hello, World!

Notice the difference between single and double quotes: single quotes do not interpolate special characters like \n while the double quotes do. There’s no mistake in using double quotes for strings without special characters, while it is better to use the appropriate quoting style when you do not expect variables in the string and when there is no need to interpolate variables.Another thing to take a look at in the examples above is that a semicolon is not required for one-line programs.

📘 How to debug Perl 6 programs

For quick tests, use the compiler in the mode of the REPL (read—eval—print loop) shell. Just run the perl6 command:

$ perl6
To exit type 'exit' or '^D'

With bigger programs, one of the following techniques helps to visualise data:

1. The say routine is used as a stand-alone function or as an object method. It works well with both scalar and aggregate data, such as arrays, hashes, or objects:

say $x;
%data.say;

2. The perl method, which returns the representation of an object in the Perl 6 syntax:

say {a => 1, b => 2}.perl; # {:a(1), :b(2)}

3. The WHAT and the ^name methods, which give you the information about the object type or class name:

my Int $x;
say $x.WHAT;  # (Int)
say $x.^name; # Int

4. The dd routine. This is a Rakudo-specific feature that dumps an object:

my @a = 1..5;
dd @a; # Array @a = [1, 2, 3, 4, 5]

🦋 109. 42 via the cubes

In the recent days, you might have seen the calculation that leads to getting an exact value of 42, the answer of Life, the Universe and Everything. Let me copy it here, using the power of Perl 6 and its arbitrary precision arithmetics , not to mention the coolness of using superscripts directly in the code.

$ time perl6 -e'say 80435758145817515³ - 80538738812075974³ + 12602123297335631³'
42
real 0m0.151s
user 0m0.173s
sys 0m0.035s

🦋 108. Basic usage of NativeCall in Perl 6

NativeCall is both a module and a technology in Perl 6 that allows you to call C functions from your Perl 6 code. Today, let’s meet the most basic usage.

Take the rand() function from the C standard library:

#include <stdio.h>
#include <stdlib.h>

int main() {    
    int r = rand();
    printf("%i\n", r);

    return 0;
}

If you are used to Perl, it may be a surprise that the program actually prints the same number every time you call it. (That’s a kind of surprise of forgotten knowledge.) Compile and run:

$ gcc rand.c

$ ./a.out 
16807

$ ./a.out 
16807

Now let’s call C’s rand() from Perl 6. Refer to the documentation of NativeCall to see the options it offers.

Your Perl 6 program may look like this:

use NativeCall;

sub c_rand() returns int32 is native('c') is symbol('rand') {*}

say c_rand();

Although the rand() function was chosen as one from the standard library that do not need parameters, the choice added a few small complications to the Perl 6 code. But it is good that we can demonstrate them all together.

Similarly to how you declare external functions in C, you need to tell Perl 6 that there is some function that it should load from an external library. The name of the library is passed in the native trait. Its argument, c, will be converted to libc, and the corresponding shared library will be searched for in the standard places on your computer.

The symbol trait tells us the original function name. It is a bit weird to me, but Rakudo cannot handle the following code where you use the same name as in C:

use NativeCall;

sub rand() returns int32 is native('c') {*}

say rand();

This is how you would declare an external C function if you didn’t want to rename it in your program. Unfortunately, Perl 6 also has rand, and we got an error:

===SORRY!=== Error while compiling rand.pl6
 Unsupported use of rand(); in Perl 6 please use rand
 at rand.pl6:5
 ------> say rand⏏();

Finally, returns int32 tells that the function returns a native 32-bit integer.

Another surprise pops up when you run our Perl 6 program. It returns a random value, and that value is different each time you call it:

$ perl6 rand.pl6 
1790432239

$ perl6 rand.pl6 
1431059869

Is it really working? It is random, but why it does not copy the behaviour of the reference C program?

Does Rakudo call srand when it starts up? A shallow investigation gives the following log comment:

nqp/MoarVM/docs/ChangeLog:+ Do not call srand() if not using rand()

Let’s go back to our test case. To make sure it works, let us create our own version of the function, which will be returning the same value again and again:

myrand.c:

int myrand() {
    return 42;
}

myrand.h:

int myrand();

Test it in pure C first:

#include "myrand.h"
#include <stdio.h>

int main() {
    int r = myrand();
    printf("%i\n", r);

    return 0;
}
$ gcc myrand-main.c myrand.c 

$ ./a.out 
42

$ ./a.out 
42

It works. Now use it from Perl 6, and the program is almost the same as before:

use NativeCall;

sub myrand() returns int32 is native('libmyrand.so') {*}

say myrand();

The main differences are the name of the function (there is no clash with Perl 6 built-in functions, and thus no need to introduce an alias) and the library name, which is a file name this time.

(This is how you create a shared library:)

$ gcc -shared -olibmyrand.so myrand.c 

If you run the updated Perl 6 program, you will only see 42 in the output.

$ perl6 myrand.pl6 
42

$ perl6 myrand.pl6 
42

This confirms that the returns int32 clause works correctly, and the value returned from the C library is understood by Perl 6.

💡 107. Odd-even sort in Perl 6

In the Odd-Even sort, or Brick sort, you take every second element and swap it with the next one if they are not sorted. Then you take the same elements and swap it with the previous element if they are not ordered. You continue until you are done.

You can formulate the algorithm a bit differently, and then you will clearly have odd and even elements: first, you compare odd elements with its neighbours, then you compare even elements. If you draw the working pairs on each step, you will see the shape resembling the layout of a brick wall.

In the first approach, let’s duplicate the code for each stage: odd and even.

sub odd-even-sort(@data) {
    my $done = False;
    while !$done {
        $done = True;
        loop (my $i = 0; $i < @data - 1; $i += 2) {
            if [>] @data[$i, $i + 1] {
                $done = False;
                @data[$i, $i + 1].=reverse;
            }
        }
        loop ($i = 1; $i < @data; $i += 2) {
            if [>] @data[$i, $i + 1] {
                $done = False;
                @data[$i, $i + 1].=reverse;
            }
        }
    }
}

my @data = 4, 5, 7, 1, 46, 78, 2, 2, 1, 9, 10;
odd-even-sort @data;
say @data;

There are two C-style loops here, both with the step 2.

It is possible to simply repeat the procedure @data.elems times, so you can remove the $done flag and use the postfix ifs.

sub odd-even-sort(@data) {
    for ^@data {
        loop (my $i = 0; $i < @data - 1; $i += 2) {
            @data[$i, $i + 1].=reverse if [>] @data[$i, $i + 1];
        }
        loop ($i = 1; $i < @data; $i += 2) {
            @data[$i, $i + 1].=reverse if [>] @data[$i, $i + 1];
        }
    }
}

Actually, if you want to use a flag, you can still do it with postfix if conditions:

sub odd-even-sort(@data) {
    my $done = False;
    while !$done {
        $done = True;
        loop (my $i = 0; $i < @data - 1; $i += 2) {
            $done--, @data[$i, $i + 1].=reverse 
                if [>] @data[$i, $i + 1];
        }
        loop ($i = 1; $i < @data; $i += 2) {
            $done--, @data[$i, $i + 1].=reverse
                if [>] @data[$i, $i + 1];
        }
    }
}

But that was the minor thing (although it is important for performance). For code beauty, we need to merge the loops, as they do the same work, just for different pairs of elements.

What if another loop with two iterations?

sub odd-even-sort(@data) {
    my $done = False;
    while !$done {
        $done = True;
        for 0..1 -> $start {
            loop (my $i = $start; $i < @data - 1 - $start; $i += 2) {
                $done--, @data[$i, $i + 1].=reverse 
                    if [>] @data[$i, $i + 1];
            }
        }
    }
}

Not bad, but what about sequences? Let us make a very verbose but quite understandable list of values that consists of two parts: even and odd indices:

sub odd-even-sort(@data) {
    my $done = False;
    while !$done {
        $done = True;
        for flat(
                (0, 2 ... @data - 2), (1, 3 ... @data - 1)
            ) -> $i {
            $done--, @data[$i, $i + 1].=reverse 
                if [>] @data[$i, $i + 1];
        }
    }
}

Of course, the for loop can go to the postfix part:

sub odd-even-sort(@data) {
    my $done = False;
    repeat {
        $done = True;
        $done--, @data[$_, $_ + 1].=reverse
            if [>] @data[$_, $_ + 1]
            for flat((0, 2 ... @data - 2), (1, 3 ... @data - 1));
    } until $done;
}

Or, if you don’t like using a flag:

sub odd-even-sort(@data) {
    while ![<=] @data {
        @data[$_, $_ + 1].=reverse if [>] @data[$_, $_ + 1]
            for flat((0, 2 ... @data - 2), (1, 3 ... @data - 1));
    }
}

(An extra comparison step can significantly slow the algorithm down.)

At this point, the code is quite compact, and if you’d like to work on it further, you have to express the sequence of odd and event numbers with less characters of code.

I wish you success! If you succeed, please share the solution with us. The source codes of all versions are available on GitHub.