Perl Weekly Challenge: Week 353
Challenge 1:
Max Words
You are given an array of sentences.
Write a script to return the maximum number of words that appear in a single sentence.
Example 1
Input: @sentences = ("Hello world", "This is a test", "Perl is great")
Output: 4
Example 2
Input: @sentences = ("Single")
Output: 1
Example 3
Input: @sentences = ("Short", "This sentence has seven words in total", "A B C", "Just four words here")
Output: 7
Example 4
Input: @sentences = ("One", "Two parts", "Three part phrase", "")
Output: 3
Example 5
Input: @sentences = ("The quick brown fox jumps over the lazy dog", "A", "She sells seashells by the seashore", "To be or not to be that is the question")
Output: 10
A Raku one-liner. We take the input from command-line arguments. Then, using
.map() we take each sentence and transform it by splitting it into words with .words() and counting the words using .elems(). At this point, we have an array
of word counts, so callinf .max() on it will give us the length of the longest
sentence which we can print out with .say().
@*ARGS.map({ .words.elems }).max.say
Perl doesn't have .words() or .max() etc. will we still be able to have a one-line solution? Happily, the answer is yes. Instead of .words() we can use
split /\s+/. scalar will count the length of the resulting array of words
just like .elems(). If we sort() the array of word counts in descending
numeric order, the 0th element will be the length of the longest sentence.
say [ sort { $b <=> $a } map { scalar split /\s+/ } @ARGV ]->[0]
Challenge 2:
Validate Coupon
You are given three arrays,
@codes,@namesand@status.Write a script to validate codes in the given array.
A code is valid when the following conditions are true:
- codes[i] is non-empty and consists only of alphanumeric characters (a-z, A-Z, 0-9) and underscores (_).
- names[i] is one of the following four categories: "electronics", "grocery", "pharmacy", "restaurant".
- status[i] is true.
Return an array of booleans indicating validity: output[i] is true if and only if codes[i], names[i] and status[i] are all valid.
Example 1
Input: @codes = ("A123", "B_456", "C789", "D@1", "E123")
@names = ("electronics", "restaurant", "electronics", "pharmacy", "grocery")
@status = ("true", "false", "true", "true", "true")
Output: (true, false, true, false, true)
Example 2
Input: @codes = ("Z_9", "AB_12", "G01", "X99", "test")
@names = ("pharmacy", "electronics", "grocery", "electronics", "unknown")
@status = ("true", "true", "false", "true", "true")
Output: (true, true, false, true, false)
Example 3
Input: @codes = ("_123", "123", "", "Coupon_A", "Alpha")
@names = ("restaurant", "electronics", "electronics", "pharmacy", "grocery")
@status = ("true", "true", "false", "true", "true")
Output: (true, true, false, true, true)
Example 4
Input: @codes = ("ITEM_1", "ITEM_2", "ITEM_3", "ITEM_4")
@names = ("electronics", "electronics", "grocery", "grocery")
@status = ("true", "true", "true", "true")
Output: (true, true, true, true)
Example 5
Input: @codes = ("CAFE_X", "ELEC_100", "FOOD_1", "DRUG_A", "ELEC_99")
@names = ("restaurant", "electronics", "grocery", "pharmacy" "electronics")
@status = ("true", "true", "true", "true", "false")
Output: (true, true, true, true, false)
The first order of business is to get the input into the script. The method I
chose was to have three command-line string arguments enclosed in "" for the three
arrays. Within each argument, the individual elements are separated by whitespace.
This way, they can easily be reconstitued into arrays with .words().
There is one added complication for @codes; if you look at example 3, there
is an empty element which would be impossible to represent if we delimited the elements with whitespace. So for @codes only, I wrapped each element in ''
and when reconstituting the array, the quotes are removed with .map() and
.subat().
my @codes = $codes.words.map({ .subst(/(^^ \') || (\' $$)/, q{}, :g) });
my @names = $names.words;
my @status = $status.words;
We also need an array to hold the validity status of each iten.
my @validity = ();
As all three arrays are the same length, we can use the indices of @codes (obtained with .keys()) as indices for the other two. Each item...
for @codes.keys -> $i {
...is tested with the criteria given in the spec. .so is added to the first
two to force boolean context so the whole statement returns True or False.
This is added to @validity.
@validity.push(
@codes[$i].match(/^^ <alnum>+ $$/).so &&
<electronics grocery pharmacy restaurant>.grep(@names[$i]).so &&
@status[$i] eq 'true'
);
}
Finally, we print the values of @validity joined by spaces.
say @validity.join(q{ });
This is the Perl version.
my @codes = map { $_ =~ s/^ \' | \' $//gx; $_ } split /\s+/, $ARGV[0];
my @names = split /\s+/, $ARGV[1];
my @status = split /\s+/, $ARGV[2];
my @validity = ();
for my $i (keys @codes) {
push @validity,
$codes[$i] =~ /^[A-Za-z0-9_]+$/ &&
(grep {$_ eq $names[$i]} qw/electronics grocery pharmacy restaurant/) &&
$status[$i] eq 'true'
? 'true'
: 'false'
;
}
say join q{ }, @validity;