This is my response to The Weekly Challenge #267.
@ints
.
1
if the product is positive, -1
if the product is
negative and 0
if product is zero.
Input: @ints = (-1, -2, -3, -4, 3, 2, 1)
Output: 1
The product -1 x -2 x -3 x -4 x 3 x 2 x 1 => 144 > 0
Example 2:
Input: @ints = (1, 2, 0, -2, -1)
Output: 0
The product 1 x 2 x 0 x -2 x -1 => 0
Example 3:
Input: @ints = (-1, -1, 1, -1, 2)
Output: -1
The product -1 x -1 x 1 x -1 x 2 => -2 < 0
#! /usr/bin/env perl6
unit sub MAIN (*@ints where all(@ints) ~~ Int && @ints.elems > 0, # [1]
:v(:$verbose));
my $product = [*] @ints; # [2]
say ": Product: $product" if $verbose;
# say $product ?? ( $product / $product.abs ) !! 0; # [3]
say + ($product <=> 0); # [4]
[1] A slurpy array to catch at leason one integer.
[2] Use the Reduction Metaoperator
[]
in combination with *
(multiplication) to
multiply all the values together.
See
docs.raku.org/language/operators#Reduction_metaoperators for more
information about the Reduction Metaoperator []
.
[3] A first try could be something like this; divide the resulting sum by the absolute value of the sum (i.e. without the sign, if any). This will give the expected result. Except when we try to divide by zero. Thus we special case that.
[4]
But it is neater to use the three way
numerical comparison operator <=>
. This operator returns «Same», «Less»
or «Equal», which will not do. But we can coerce those values to «-1», «0» and
«1» with the Numeric Coercion Prefix +
.
See
docs.raku.org/routine/<=>
for more information about the Numeric three-way comparator <=>
.
See
docs.raku.org/routine/+ for
more information about the Numeric Coercion Prefix Operator +
.
Running it:
$ ./product-sign -- -1 -2 -3 -4 3 2 1
1
$ ./product-sign 1 2 0 -2 -1
0
$ ./product-sign -- -1 -1 1 -1 2
-1
Looking good.
With verbose mode:
$ ./product-sign -v -- -1 -2 -3 -4 3 2 1
: Product: 144
1
$ ./product-sign -v 1 2 0 -2 -1
: Product: 0
0
$ ./product-sign -v -- -1 -1 1 -1 2
: Product: -2
-1
$str
, and a 26-items
array @widths
containing the width of each character from a to z
.
100
width
units on a line.
Input: $str = "abcdefghijklmnopqrstuvwxyz"
@widths = (10,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10)
Output: (3, 60)
Line 1: abcdefghij (100 pixels)
Line 2: klmnopqrst (100 pixels)
Line 3: uvwxyz (60 pixels)
Example 2:
Input: $str = "bbbcccdddaaa"
@widths = (4,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10)
Output: (2, 4)
Line 1: bbbcccdddaa (98 pixels)
Line 2: a (4 pixels)
#! /usr/bin/env perl6
unit sub MAIN ($str,
*@widths where all(@widths) ~~ UInt # [1]
&& @widths.elems == 26, # [1a]
:v(:$verbose));
my %width; # [2]
for 'a' .. 'z' -> $letter # [3]
{
%width{$letter} = @widths.shift.Int; # [3a]
}
say ": Widths: { %width.raku }" if $verbose;
my $line = ""; # [4]
my $width = 0; # [5]
my $id = 0; # [6]
for $str.comb -> $letter # [7]
{
if $width + %width{$letter} > 100 # [8]
{
$id++; # [8a]
say ": Line $id: $line ($width pixels)" if $verbose;
$line = ""; # [8b]
$width = 0; # [8c]
}
$line ~= $letter; # [9]
$width += %width{$letter}; # [10]
}
if $line # [11]
{
$id++; # [12]
say ": Line $id: $line ($width pixels)" if $verbose;
}
say "($id, $width)"; # [13]
[1] Ensure exactly 26 unsigned integers.
[2] We are going to calculate the width of all the characters, for easier lookup later on.
[3] Iterate over the letters, settng up the width of each one by shifting off the first (leftmost) remaining width value.
[4] The line is initially empty.
[5] The width of an empty line is zero.
[6] The line counter (or ID).
[7] Iterate over each letter in the input string, with comb
.
See docs.raku.org/routine/comb for more information about comb
.
[8] Will the length of the current letter exceed the limit if we were to add it to the line? If so, start a new line (by increasing the counter in [8a], by resetting the line itself in [8b] and resetting the line width in [8b]).
[9] Add the current letter to the line (as it will not exceed the limit).
[10] And add the width of that letter to the total width.
[11] After the loop, check if we have any unfinished lines.
[12] If so, increase the line counter.
[13] Print the number of lines, and the current width (i.e. the width of the last line).
Running it:
$ ./line-counts abcdefghijklmnopqrstuvwxyz 10 10 10 10 10 10 10 10 10 \
10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
(3, 60)
$ ./line-counts -v bbbcccdddaaa 4 10 10 10 10 10 10 10 10 10 10 10 10 10 \
10 10 10 10 10 10 10 10 10 10 10 10
(2, 4)
Looking good.
With verbose mode:
$ ./line-counts -v abcdefghijklmnopqrstuvwxyz 10 10 10 10 10 10 10 10 10 \
10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
: Widths: {:a(10), :b(10), :c(10), :d(10), :e(10), :f(10), :g(10), :h(10),
:i(10), :j(10), :k(10), :l(10), :m(10), :n(10), :o(10), :p(10), :q(10),
:r(10), :s(10), :t(10), :u(10), :v(10), :w(10), :x(10), :y(10), :z(10)}
: Line 1: abcdefghij (100 pixels)
: Line 2: klmnopqrst (100 pixels)
: Line 3: uvwxyz (60 pixels)
(3, 60)
$ ./line-counts -v bbbcccdddaaa 4 10 10 10 10 10 10 10 10 10 10 10 10 10 \
10 10 10 10 10 10 10 10 10 10 10 10
: Widths: {:a(4), :b(10), :c(10), :d(10), :e(10), :f(10), :g(10), :h(10), \
:i(10), :j(10), :k(10), :l(10), :m(10), :n(10), :o(10), :p(10), :q(10), \
:r(10), :s(10), :t(10), :u(10), :v(10), :w(10), :x(10), :y(10), :z(10)}
: Line 1: bbbcccdddaa (98 pixels)
: Line 2: a (4 pixels)
(2, 4)
And that's it.