### Perl Weekly Challenge: Week 266

#### Challenge 1:

Uncommon Words

You are given two sentences, `\$line1` and `\$line2`.

Write a script to find all uncommmon words in any order in the given two sentences. Return `('')` if none found.

A word is uncommon if it appears exactly once in one of the sentences and doesn’t appear in other sentence.

##### Example 1
``````Input: \$line1 = 'Mango is sweet'
\$line2 = 'Mango is sour'
Output: ('sweet', 'sour')
``````
##### Example 2
``````Input: \$line1 = 'Mango Mango'
\$line2 = 'Orange'
Output: ('Orange')
``````
##### Example 3
``````Input: \$line1 = 'Mango is Mango'
\$line2 = 'Orange is Orange'
Output: ('')
``````

This is yet another problem we can solve using Raku's support for set operations.

First we take each input argument and split it into a list of words with `.words()`. We need only the words that occur once in each list and the easiest way to do that is to convert the list into a `Bag` which will consist of all the unique words and the number of times they occur, then filter that bag with `grep()` to find elements with a value of 1 which would mean that element only occurred once in the bag. After that we no longer need the frequencies (values), just the words (keys) so we select only them with `.map()`. Performing this process on `\$line1` and `\$line2` results in two lists `@words1` and `@words2`.

``````my @words1 = \$line1.words.Bag.grep({ \$_.value == 1; }).map({ \$_.key });
my @words2 = \$line2.words.Bag.grep({ \$_.value == 1; }).map({ \$_.key });
``````

Applying thhe symmetric difference operator `⊖` to `@words1` and `@words2` gives us the solution. The rest of this line is only for printing the output in the style of the examples.

``````say q{(}, ((@words1 ⊖ @words2).map({ "'{\$_.key}'" }).join(q{, }) || "''"), q{)};
``````

(Full code on Github.)

Perl is not blessed with Raku's wide range of exotic types and operators so we have to provide the functionality ourselves. Last week I wrote a function called `makeBag()` which can be repurposed here.

Notice the signature. Ubuntu 24.04 LTS came out this week and now that I've upgraded, I have Perl 5.38 which now makes the hitherto experimental support for subroutine signatures an official part of the language.

``````sub makeUniqueBag(@array) {
my %bag;
for my \$c (@array) {
\$bag{\$c}++;
}
``````

There's no need to slavishly copy previous code. With the addition of an additional `grep()` here, I can find the unique words. That's why this routine is called `makeUniqueBag()`.

``````    return grep { \$bag{\$_} == 1 } keys %bag;
}
``````

We need a replacement for the `⊖` operator and I thought it would be difficult but it was surprisingly simple.

Subroutine signature again. Unfortunately Perl doesn't support more than one array parameter so we need to pass them by reference like in the good old days.

``````sub symmetricDifference(\$set1, \$set2) {
``````

All we need to do is combine the two input arrays into one...

``````    my @all = (@{\$set1}, @{\$set2});
``````

...and run `makeUniqueBag()` on it. Voila! Symmetric difference.

``````    return makeUniqueBag(@all);
}
``````

Now we have these, we can just port the Raku algorithm.

``````my @words1 = makeUniqueBag(split /\s+/, @ARGV[0]);
my @words2 = makeUniqueBag(split /\s+/, @ARGV[1]);

say q{(}, ((join q{, }, map { "'\$_'" } symmetricDifference(\@words1, \@words2)) || "''"), q{)};
``````

(Full code on Github.)

#### Challenge 2:

X Matrix

You are given a square matrix, `\$matrix`.

Write a script to find if the given matrix is `X Matrix`.

A square matrix is an X Matrix if all the elements on the main diagonal and antidiagonal are non-zero and everything else are zero.

##### Example 1
``````Input: \$matrix = [ [1, 0, 0, 2],
[0, 3, 4, 0],
[0, 5, 6, 0],
[7, 0, 0, 1],
]
Output: true
``````
##### Example 2
``````Input: \$matrix = [ [1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
Output: false
``````
##### Example 3
``````Input: \$matrix = [ [1, 0, 2],
[0, 3, 0],
[4, 0, 5],
]
Output: true
``````

The `Main()` function of the Raku solution consists of only one line. Each row of the matrix is input as a command-line argument. The columns in those rows are separated by whitespace. So we can easily convert the input into a 2D array with `.map()` and `.words()`. This 2D array is passed into a function called `checkMatrix()` which does all the heavy lifting. Its' results (`True` or `False`) will be output with `say()`.

``````say checkMatrix(@args.map({ [\$_.words] }));
``````

This is `checkMatrix()`:

``````sub checkMatrix(@matrix) {
``````

We define two variables which will represent the left or right side of a matrix row. For the first row they will be the first and last columns of the row. They will also be the columns containing the diagonal and antidiagonal on that row.

``````    my \$left = 0;
my \$right = @matrix[0].end;
``````

Now for every row...

``````    for @matrix.keys -> \$row {
``````

...and every column in that row...

``````        for @matrix[\$row].keys -> \$col {
``````

...we check if it is the `\$left` or the `\$right`. If it is...

``````            if \$col == \$left || \$col == \$right {
``````

we check that it does_not contain a 0. If it does, this is not an X matrix so we return `False`.

``````                if @matrix[\$row;\$col] == 0 {
return False;
}
``````

If the column was not `\$left` or `\$right` we check that it does contain a 0. If it does not, this is not an X matrix so we return `False`.

``````            } else {
if @matrix[\$row;\$col] != 0 {
return False;
}
}
}
``````

After each row is processed we increment the value of `\$left` and decrement the value of `\$right` and go on to the next row.

``````        \$left++;
\$right--;
}
``````

If we are still here after all rows have processed, this is an X matrix so we return `True`.

``````    return True;
}
``````

(Full code on Github.)

This is the Perl version. It is mostly the same except we are using `1` and `undef` instead of `True` and `False`.

``````sub checkMatrix(@matrix) {
my \$left = 0;
my \$right = scalar @{\$matrix[0]} - 1;

for my \$row (keys @matrix) {
for my \$col (keys @{\$matrix[\$row]}) {
if (\$col == \$left || \$col == \$right) {
if (\$matrix[\$row][\$col] == 0) {
return undef;
}
} else {
if (\$matrix[\$row][\$col] != 0) {
return undef;
}
}
}
\$left++;
\$right--;
}

return 1;
}
``````

This bit was one line in Raku but I couldn't make it work the same way (due to `map()` being in scalar context maybe?) so I just assigned the matrix to an explicit variable and passed that to `checkMatrix()`.

``````my @matrix = map { [split /\s+/, \$_] } @ARGV;
say checkMatrix(@matrix) ? 'true' : 'false';
``````

(Full code on Github.)