This is my response to the Perl Weekly Challenge #083.
Input: $S = "The Weekly Challenge"
Output: 6
Example 2
Input: $S = "The purpose of our lives is to be happy"
Output: 23
#! /usr/bin/env raku
unit sub MAIN (Str $S where $S.words.elems >= 3, # [1]
:v($verbose));
my @words = $S.words; # [2]
my $s = @words[1.. @words.end -1].join; # [3]
say ": $s" if $verbose;
say $s.chars; # [4]
[1] The where
clause ensures that we get at least three words.
[2] Split the string into words, according to what words
considers words...
[3] Get all the words, except the first and last, and join them together to a single "word".
[4] Print the length of that "word".
See my
Common Frequency with Raku article, or
docs.raku.org/routine/words for
more information about words
.
Running it on the examples:
$ ./words-length "The Weekly Challenge"
6
$ ./words-length "The purpose of our lives is to be happy"
23
With verbose mode:
$ ./words-length -v "The Weekly Challenge"
: 'Weekly'
6
$ ./words-length -v "The purpose of our lives is to be happy"
: 'purposeofourlivesistobe'
23
A string with too few words causes a usage error:
$ ./words-length -v "The Challenge"
Usage:
./words-length [-v=<Any>] <S>
@A
of positive numbers.
Input: @A = (3, 10, 8)
Output: 1
Explanation
Flipping the sign of just one element 10 gives the result 1
i.e. (3) + (-10) + (8) = 1
Example 2
Input: @A = (12, 2, 10)
Output: 1
Explanation
Flipping the sign of just one element 12 gives the result 0
i.e. (-12) + (2) + (10) = 0
This time I'll start with the output of the verbose mode, and explain it from there.
$ ./flip-array -v 3 10 8
: Candidate [3, 10, 8] with sum 21 # [1]
: Candidate [3, 10, -8] with sum 5
: Candidate [3, -10, 8] with sum 1
: Candidate [3, -10, -8] with sum -15
: Candidate [-3, 10, 8] with sum 15
: Candidate [-3, 10, -8] with sum -1
: Candidate [-3, -10, 8] with sum -5
: Candidate [-3, -10, -8] with sum -21
: Lowest non-negative sum: 1 # [2]
: Match 0: [3, -10, 8] with 1 flip(s) # [3]
: Flips: [1] # [4]
1 # [5]
$ ./flip-array -v 12 2 10
: Candidate [12, 2, 10] with sum 24 # [1]
: Candidate [12, 2, -10] with sum 4
: Candidate [12, -2, 10] with sum 20
: Candidate [12, -2, -10] with sum 0
: Candidate [-12, 2, 10] with sum 0
: Candidate [-12, 2, -10] with sum -20
: Candidate [-12, -2, 10] with sum -4
: Candidate [-12, -2, -10] with sum -24
: Lowest non-negative sum: 0 # [2]
: Match 0: [12, -2, -10] with 2 flip(s) # [3]
: Match 1: [-12, 2, 10] with 1 flip(s)
: Flips: [2, 1] # [4]
1 # [5]
[1] The first step («Candidate …») is computing all the permutaions we can get by flipping the sign on the values. Note the sum at the end of the line. We are storing the candidates in a hash, with the sum as the key.
[2] The second step («Lowest …») is computing the lowest non-negative sum.
[3] In the third step we iterate over the candidates with the number of flips from the previous step, computing the number of flips on each one.
[4] The fourth step gives us a list of the number of flips.
[5] Print the lowest number (of flips).
File: flip-array (partial)
#! /usr/bin/env raku
subset Positive of Numeric where * > 0; # [11]
unit sub MAIN (*@A where @A.elems > 0 && all(@A) ~~ Positive, # [11]
:v($verbose));
my %sum; # [12]
do_it( (), @A); # [13]
my $lowest-sum = %sum.keys.grep( * >= 0).min; # [14]
say ": Lowest non-negative sum: $lowest-sum" if $verbose;
my @flip; # [15]
for @(%sum{$lowest-sum}) -> @candidate # [16]
{
state $index = 0; # [17]
@flip[$index] = @candidate.grep( * < 0).elems; # [18]
say ": Match $index: [{ @candidate.join(", ") }] with @flip[$index] flip(s)"
if $verbose;
$index++; # [17a]
}
say ": Flips: [{ @flip.join(", ") }]" if $verbose;
say @flip.min; # [19]
[11] Ensure that all the elements are positive. The challenge calls them numbers, so we support all positive numeric values. (Even if the examples only use integers.)
[12] We are going to store the permutuations we get when we flip the sign of none to all the values here.
[13] This recursive procedure builds up all the permutatations, and places
them in %sum
.
[14] Get the lowest number (key in the hash), after we have removed those that are negative.
[15] We are going to store the number of flips for the candidates here.
[16] For the given key, take the value - as an array - and iterate over it.
[17] I could have declared the variable before the loop, but a state variable ensures that it is only available inside the loop.
[18] Count the number of flips, which is the sum of all the negative numbers.
[19] The smallest number (of flips) is the answer.
See
docs.raku.org/syntax/state
more information about state
.
sub do_it (@left, @right is copy) # [21]
{
my $current = @right.shift; # [22]
unless defined $current # [23]
{
say ": Candidate [{ @left.join(", ") }] with sum {@left.sum }" if $verbose;
%sum{@left.sum}.push: @left; # [24]
return; # [24a]
}
do_it((@left, $current).flat, @right); # [25]
do_it((@left, -$current).flat, @right); # [25a]
}
[21] The recursive procedure. Note the is copy
on the second parameter, so that we can change it (the local copy).
The first list is the numbers we have flipped, and the second is the one that we
haven't flipped yet.
[22] Get the next (unflipped) number.
[23] If we are done ([22] failed),
[24] • add the list to the candidate list and we are done with this recursion [24a].
[25] Add the current value, unflipped and flipped [25a] and go on recursively on both.
See
docs.raku.org/type/Signature#index-entry-trait_is_copy
more information about is copy
.
Running it (without verbose mode) gives the expected result:
$ ./flip-array 3 10 8
1
$ ./flip-array 12 2 10
1
Non-Integers work out fine. Even rational numbers like «1/3»:
./flip-array -v 1 2/3 1/3
: Candidate [1, 2/3, 1/3] with sum 2
: Candidate [1, 2/3, 1/3] with sum 1.333333
: Candidate [1, 2/3, 1/3] with sum 0.666667
: Candidate [1, 2/3, 1/3] with sum 0
: Candidate [-1, 2/3, 1/3] with sum 0
: Candidate [-1, 2/3, 1/3] with sum -0.666667
: Candidate [-1, 2/3, 1/3] with sum -1.333333
: Candidate [-1, 2/3, 1/3] with sum -2
: Lowest non-negative sum: 0
: Match 0: [1, 2/3, 1/3] with 2 flip(s)
: Match 1: [-1, 2/3, 1/3] with 1 flip(s)
: Flips: [2, 1]
1
Except for a very obvious problem. The sign is not shwown for the rational numbers. The sum and the number of flips are calculated correctly, so this is only a presentation error.
And that's it.