The Weekly Challenge ‐ Perl and Raku

CY's Take on The Weekly Challenge #131

If you want to challenge yourself on programming, especially on Perl and/or Raku, go to https://theweeklychallenge.org, code the latest challenges, submit codes on-time (by GitHub or email).

Do tell me, if I am wrong or you strongly oppose my statements!

It's time for challenges in Week #131 !

And here comes two nice tasks submitted by teammates.


image info

Task 1: Consecutive Arrays

I like this task. Its simplicity makes it a perfect testing performance of showing the power of compactness of a language (for example, the Raku solutions "in a [Twitter] tweet" by Mr Markus Holzer) or learning a new language.

My code in Perl and Julia:

In Perl:

sub consec {
    my @a = @_;
    my @list = ([ $a[0] ],);
    for my $i (1..$#a) {
        if ($a[$i] == $a[$i-1] + 1) {
            push $list[-1]->@*, $a[$i];
        }
        else {
            push @list, [$a[$i]];
        }
    }
    return \@list;
}

In Julia:

function consecutive(a)
    list = [ [ a[1] ], ]
    for i = 2:length(a)
        if a[i] == a[i-1]+1
            push!(list[end], a[i])
        else
            push!(list, [ a[i] ])
        end
    end
    return list
end

In Julia, it is required that elements of an array being the same type. (I don't know the technical term...) (Wrong.)

Also note that the array of Julia starts indexing from 1 instead of indexing from 0.

Since this is one of my early codes in Julia, I encountered some obstacles. It would be great to learn how to read the feedback of REPL in Julia or any interpreter/compiler of any programming languages for debugging.

Mentioning the usefulness of feedback, it is worth the time to note down my solution for Task 2.

Task 2: Find Pairs

The abundant choice of delimiters is a special feature of Perl.

It's fun to see how many symbols can be used as delimiters:

$ perl -E '$_ = "cba"; tr^abc^def^; say'
fed
$ perl -E '$_ = "cba"; tr$abc$def$; say'
fed
$ perl -E '$_ = "cba"; tr@abc@def@; say'
fed
$ perl -E '$_ = "cba"; tr+abc+def+; say'
fed
$ perl -E '$_ = "cba"; tr-abc-def-; say'
fed
$ perl -E '$_ = "cba"; tr&abc&def&; say'
fed

Therefore the Task 2 code I start it with

my @open_uni  = qw/ ( [ { < /;
my @close_uni = qw/ ) ] } > /;
my %partner = ( ')'=>'(', ']'=>'[', '}'=>'{', '>' => '<' );
my @neutral_uni = qw{ , . ? ! / \ };
push @neutral_uni, qw{ @ # $ & + - ^ * % " ' }; # dangerous zone

The given task statement does not state the practical side of the task and I guess it is about tidying the pairing of delimiting symbols. Hence I decide to run an extra mile, write something with warnings and/or feedbacks.

Like this (extracted from the final production):

Test Case 1b
Delimiters   : (){}
Search String: for ($a..$b} (cannot_do_sth;}
    ((
    }}
Warning: ( at position 4 may not close appropriately.
Warning: } at position 11 may not be corresponding to an opening delimiter.
Feedback: It is recommended that you check other delimiters as well.

Originally I ambitiously intended to write the code for multi-line strings! Time does not allow. Ooops.

For the ease of explanation, let us see the output of the final product first:

$ perl ch-2.pl '(){}**' '(*Hello*)'
Delimiters   : (){}**
Search String: (*Hello*)
    (**
    **)

=========================================
Example 1
Delimiters   : ""[]()
Search String: "I like (parens) and the Apple ][+" they said.
    "(["
    ")]"
Warning: " at position 0 may not close appropriately.
Warning: ] at position 31 may not be corresponding to an opening delimiter.
Feedback: It is recommended that you check other delimiters as well.

Example 2
Delimiters   : **//<>
Search String: /* This is a comment (in some languages) */ <could be a tag>
    /**/<
    /**/>

Test Case 1a
Delimiters   : (){}
Search String: for ($a..$b) {do_sth;}
    ({
    )}

Test Case 1b
Delimiters   : (){}
Search String: for ($a..$b} (cannot_do_sth;}
    ((
    }}
Warning: ( at position 4 may not close appropriately.
Warning: } at position 11 may not be corresponding to an opening delimiter.
Feedback: It is recommended that you check other delimiters as well.

Test Case 2a
Delimiters   : (){}**
Search String: ( ilovePerl()*) {bad;}
    ((*{
    )*)}
Warning: * at position 13 may not close appropriately.
Warning: ) at position 14 may not be corresponding to an opening delimiter.
Feedback: It is recommended that you check other delimiters as well.

Test Case 2b
Delimiters   : (){}**
Search String: (*ilovePerl()*) {good;}
    (*(*{
    *)*)}

Test Case 3
Delimiters   : <>
Search String: <html><head><title>HELLO</title></head></html>
    <<<<<<
    >>>>>>

Test Case 4a
Delimiters   : **//\\
Search String: */layer/*
    *//*
    *//*

Test Case 4b
Delimiters   : **//\\
Search String: */wrong layer*/
    */*/
    */*/
Warning: delimiter(s) do not open or close appropriately:
Delimiters: * / * /
Positions: 0 1 13 14
And the function find_pair is called as follows:
say "Example 2";
find_pair(
  [qw' ** // <> '],
  '/* This is a comment (in some languages) */ <could be a tag>'
);

The implementation of find_pair is more than 100 lines so I just put part of it here.

sub find_pair {
    my %open_found;
    my %close_found;
    my %neutral_found;
    my @char = split //, $_[1];
    my @delimiters = split //, (join "", $_[0]->@*);

    for my $pair (@{$_[0]}) {
        my $s_head = substr($pair, 0, 1);
        my $s_tail = substr($pair, 1, 1);
        $open_found{$s_head} = []
            if any { $_ eq $s_head } (map {substr($_, 0, 1)} @open_uni); 
        #...
    }

    for my $i (0..$#char) {
        my $c = $char[$i];
        push $open_found{$c}->@*, $i if defined($open_found{$c});
        #...
    }


    my @open_positions;
    my @close_positions;
    my @all_positions;

    for (values %open_found, values %neutral_found) {
        push @open_positions, $_->@*;
    }

    #...
    @all_positions = sort {$a<=>$b} @all_positions;
    my @stack;
    my @waiting_to_be_closed;   # variable for warning message
    my $early_warn;   # variable for warning message

    for my $p (@all_positions) {
        my $c = $char[$p];
        if (defined($open_found{$c})) {
            push @stack, $c;
            push @waiting_to_be_closed, $p;
        }
        #...
    }

    if (!$early_warn && scalar @stack != 0
        && !defined($close_found{$stack[-1]})
       ) {
        say "Warning: delimiter(s) do not open or close appropriately:";
        say "Delimiters: @stack";
        say "Positions: ", "@waiting_to_be_closed";
    }
    if ($early_warn) {
        say "Feedback: It is recommended that you check other ",
            "delimiters as well.";
    }

    say "";
}

Final note: I had thought to skip this task due to the constraint of time. After I finished it last night, I think it's worth the effort (but still can improve further... some ideas: e.g. using edit distance to give suggestions). If you have seen my Twitter, you probably know why I start to use Julia ‐ I gonna work on making math animations for a few months. This is going to be my first formal programming job! In addition, I got admitted to a part-time program in Information Technology ‐ I will start school in mid-Oct, having 9.5 hr of lecture time per week. However, like, for the heavy coding (in TWC tasks) of nearly 200 lines of code of Task 2, I feel difficult to write down the flow here, or comment the flow within codes. There is still large room of improvement for CY as a coder. Need to learn some time management or "energy management" in order to balance different aspects of my life. One of the obvious hurdles is in December, when the Advent of Code event gonna be held.

Stay alert and healthy! □


The image of the flat helix is made by me via Processing. I declare here that I release it as a public domain image.

Except from images and codes from other personnels, the content of this blogpost is released under a copyleft spirit. One may share (full or partial) content of this blogpost on other platform if you share it under the free and open content spirit.

link for CY's full codes: ch-1.pl, ch-1.jl, ch-2.pl


Contact on twitter: @e7_87.

Discuss via GitHub issues: here.

Email: fungcheokyin at gmail.com

Created Date: 26th September, 2021. Updated: 27th September, 2021.