Perl Weekly Challenge: Week 42

The last two months of 2019 were full of, let us say, "interesting", obstacles to my participation in the challenge but I'm back now and ready for more Perl and Raku.

Challenge 1:

Octal Number System

Write a script to print decimal number 0 to 50 in Octal Number System.

For example:

    Decimal 0 = Octal 0
    Decimal 1 = Octal 1
    Decimal 2 = Octal 2
    Decimal 3 = Octal 3
    Decimal 4 = Octal 4
    Decimal 5 = Octal 5
    Decimal 6 = Octal 6
    Decimal 7 = Octal 7
    Decimal 8 = Octal 10

and so on.

Fortunately, Mohammed has eased my re-entry with an easy task. This can be done as a one-liner. Here is the Perl version:

perl -e 'map { printf "Decimal %d = Octal %o\n", $_, $_ } (0 .. 50);'

(Full code on Github.)

...and this is Raku:

perl6 -e '[0 .. 50].map({ printf("Deciamal %d = Octal %o\n", $_, $_) });'

(Full code on Github.)

Challenge 2:

Balanced Brackets

Write a script to generate a string with random number of ( and ) brackets. Then make the script validate the string if it has balanced brackets.

For example:

    () - OK
    (()) - OK
    )( - NOT OK
    ())() - NOT OK

There are two interesting parts to this problem; one, randomly generating sequences of brackets, and two, finding if they are balanced or not.

I did not want to make the sequences too long as if they were purely randomly chosen, it would substantially increase the chance of the sequence being unbalanced. Also I didn't want the sequence to be of odd length as that would automatically mean it was unbalanced. So in all, what I wanted is for sequences which are 2, 4, or 6 brackets only.

In perl I did it like this:

my @brackets;

for (1 .. ((int rand 3) + 1) * 2) {
    push @brackets, (int rand 2 ? '(' : ')');
}

int rand 3 gives a number between 0 to 2. Adding 1 makes it 1 to 3 and mutiplying by 2 gives 2, 4, or 6.

Inside the loop, int rand 2 gives the values 0 or 1 which are used to select the left or right bracket which is then added to the @brackets array.

This is the Raku version:

for 1 .. (2, 4, 6).pick {
    @brackets.push(Bool.pick ?? '(' !! ')');
}

Raku has the nifty .pick method which chooses one of a list of values. Much simpler. For choosing the bracket too, .pick can be used. In this case, it is called directly on the Bool class to give a True or False value.

Now we have a sequence of brackets, we can do the second task, determining if the brackets are balanced. The simplest way to do that is with a stack. When we see a left bracket, it is pushed onto the stack. When we see a right bracket, it is popped off again. If after examining the entire sequence, the stack is empty, it means the brackets are balanced. In Perl it looks like this:

sub isBalanced {
    my ($brackets) = @_; 
    my @stack;

    map {
        if ( $brackets->[$_] eq '(' ) {
            push @stack, '(';
        } else {
            if (!scalar @stack) {
                return undef;
            }
            pop @stack;
        }
    } (0 .. scalar @{$brackets} - 1); 

    return scalar @stack == 0;
}

One small optimization is that if we attempt to pop an empty stack, we can immediately return false because we know the sequence is unbalanced. (E,g, this can happen if the sequence begins with an end bracket.) As perl does not have a true boolean data type, the function returns undef which is always a false value in boolean context.

This is the Raku version:

sub isBalanced(@brackets) {
    my @stack;

    (0 ..^ @brackets.elems).map({
        if @brackets[$_] ~~ '(' {
            @stack.push( '(' );
        } else {
            if !@stack.elems {
                return False;
            }
            @stack.pop;
        }
    });

    return @stack.elems == 0;
}

it is mostly the same but Raku has small conveniences which make the code simpler and more legible such as proper function prototypes, a boolean type and the ability to exclude one or both ends of a range (^.., ..^ which is used here, or ^..^)

(Full Perl code on Github.)

(Full Raku code on Github.)