Perl Weekly Challenge: Week 239

Challenge 1:

Same String

You are given two arrays of strings.

Write a script to find out if the word created by concatenating the array elements is the same.

Example 1
Input: @arr1 = ("ab", "c")
       @arr2 = ("a", "bc")
Output: true

Using @arr1, word1 => "ab" . "c" => "abc"
Using @arr2, word2 => "a" . "bc" => "abc"
Example 2
Input: @arr1 = ("ab", "c")
       @arr2 = ("ac", "b")
Output: false

Using @arr1, word1 => "ab" . "c" => "abc"
Using @arr2, word2 => "ac" . "b" => "acb"
Example 3
Input: @arr1 = ("ab", "cd", "e")
       @arr2 = ("abcde")
Output: true

Using @arr1, word1 => "ab" . "cd" . "e" => "abcde"
Using @arr2, word2 => "abcde"

Here's a Raku one-liner to solve this task. The biggest issue is how to input the two arrays in a command-line script. I chose to make each one a string in which each element is separated by a comma and a space. So for example, the arguments for example 2 would look like: "ab, c" "ac, b"

Now all our code needs to do is .split() those arguments back into arrays, .join() them without spaces and compare them for equality. If they are equal, say() will print out True or otherwise, False.

say @*ARGS[0].split(q{, }).join eq @*ARGS[1].split(q{, }).join

(Full code on Github.)

The Perl version would have been a bit too unwieldy to make into a one-liner so I spelled everything out.

my @arr1 = split q{, }, $ARGV[0];
my @arr2 = split q{, }, $ARGV[1];

say() neads 0+ in front of it to prevent a warning about it being used as a function (which it is isn't it?? but anyway...) and we have to explicitly print true and false because they aren't keywords like in Raku.

say 0+((join q{}, @arr1) eq (join q{}, @arr2)) ? 'true' : 'false';

(Full code on Github.)

Challenge 2:

Consistent Strings

You are given an array of strings and allowed string having distinct characters.

A string is consistent if all characters in the string appear in the string allowed.

Write a script to return the number of consistent strings in the given array.

Example 1
Input: @str = ("ad", "bd", "aaab", "baa", "badab")
       $allowed = "ab"
Output: 2

Strings "aaab" and "baa" are consistent since they only contain characters 'a' and 'b'.
Example 2
Input: @str = ("a", "b", "c", "ab", "ac", "bc", "abc")
       $allowed = "abc"
Output: 7
Example 3
Input: @str = ("cc", "acd", "b", "ba", "bac", "bad", "ac", "d")
       $allowed = "cad"
Output: 4

Strings "cc", "acd", "ac", and "d" are consistent.

This time the Perl solution is a one-liner and Raku is longer so let's start with Perl.

The first command-line argument becomes the allowed string and the rest of the arguments become an array of strings to check for consistency. Frustratingly, you can't create this array with an array slice like @ARGV[1 .. -1] so we use the splice() function. The allowed string is interpolated into a regular expression which grep() uses to filter out consistent strings. scalar() in array context counts how such strings were found and say() prints the result.

say scalar grep {/^["$ARGV[0]"]+$/} splice @ARGV,1

(Full code on Github.)

In theory I should be able to translate the Perl code into one line of Raku but try as I might, I was not able to interpolate $allowed into a character class without errors.

sub MAIN(
    $allowed,
    *@str
) {

But oddly enough, if I did it as two lines where the first creates a ragular expression which interpolates $allowed into a character class...

    my $class = "<[$allowed]>";

...and the second line interpolates that regular expression into another one, it works.

    @str.grep({ /^ <$class>+ $/ }).elems.say;
}

(Full code on Github.)

After all this time, I still don't understand regular expressions in Raku very well.