aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2023-05-28 22:55:27 +1000
committerPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2023-05-28 22:55:27 +1000
commit633a8273c02eb7c9ea43959e09fcb81b8f284e9b (patch)
tree5fa197f85fc4fa5de48cc2ac9de2f7a8c24deb53
parentbcc11a8dc2fa51933b649f20cf0a3325b9193b6d (diff)
downloadperlweeklychallenge-club-633a8273c02eb7c9ea43959e09fcb81b8f284e9b.tar.gz
perlweeklychallenge-club-633a8273c02eb7c9ea43959e09fcb81b8f284e9b.tar.bz2
perlweeklychallenge-club-633a8273c02eb7c9ea43959e09fcb81b8f284e9b.zip
Perl & Raku solutions to Task 1 for Week 218
-rw-r--r--challenge-218/athanasius/perl/ch-1.pl207
-rw-r--r--challenge-218/athanasius/raku/ch-1.raku213
2 files changed, 420 insertions, 0 deletions
diff --git a/challenge-218/athanasius/perl/ch-1.pl b/challenge-218/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..9e5497df12
--- /dev/null
+++ b/challenge-218/athanasius/perl/ch-1.pl
@@ -0,0 +1,207 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 218
+=========================
+
+TASK #1
+-------
+*Maximum Product*
+
+Submitted by: Mohammad S Anwar
+
+You are given a list of 3 or more integers.
+
+Write a script to find the 3 integers whose product is the maximum and return
+it.
+
+Example 1
+
+ Input: @list = (3, 1, 2)
+ Output: 6
+
+ 1 x 2 x 3 => 6
+
+Example 2
+
+ Input: @list = (4, 1, 3, 2)
+ Output: 24
+
+ 2 x 3 x 4 => 24
+
+Example 3
+
+ Input: @list = (-1, 0, 1, 3, 1)
+ Output: 3
+
+ 1 x 1 x 3 => 3
+
+Example 4
+
+ Input: @list = (-8, 2, -9, 0, -4, 3)
+ Output: 216
+
+ -9 × -8 × 3 => 216
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2023 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. If $VERBOSE is set to a true value (the default), the output is followed by
+ an explanation of the result.
+
+=cut
+#===============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use List::MoreUtils qw( part reduce_1 );
+use Regexp::Common qw( number );
+use Test::More;
+
+const my $VERBOSE => 1;
+const my $USAGE =>
+"Usage:
+ perl $0 [<list> ...]
+ perl $0
+
+ [<list> ...] A list of 3 or more integers\n";
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 218, Task #1: Maximum Product (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ my $args = scalar @ARGV;
+
+ if ($args == 0)
+ {
+ run_tests();
+ }
+ elsif ($args < 3)
+ {
+ error( "Expected 0 or 3+ command-line arguments, found $args" )
+ }
+ else
+ {
+ / ^ $RE{num}{int} $ /x or error( qq[Invalid integer "$_"] ) for @ARGV;
+
+ printf "Input: \@list = %s\n", join ', ', @ARGV;
+
+ my $solution = [];
+ my $max_product = find_max_product( \@ARGV, $solution );
+
+ print "Output: $max_product\n";
+
+ printf( "\n%s = $max_product\n", join( ' x ', @$solution ) ) if $VERBOSE;
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find_max_product
+#-------------------------------------------------------------------------------
+{
+ my ($list, $soln) = @_;
+ my @partition = part { $_ >= 0 } @$list; # Partition the list
+ my @negatives = @{ $partition[ 0 ] // [] };
+ my @positives = @{ $partition[ 1 ] // [] };
+ my $n_negatives = scalar @negatives;
+ my $n_positives = scalar @positives;
+
+ @negatives = sort { $a <=> $b } @negatives; # Ascending
+ @positives = sort { $b <=> $a } @positives; # Descending
+
+ if (scalar @$list == 3 || $n_negatives < 2 || $n_positives == 0)
+ {
+ @$soln = (sort { $a <=> $b } @$list )[ -3 .. -1 ];
+ }
+ elsif ($n_positives < 3)
+ {
+ @$soln = (@negatives[ 0 .. 1 ], $positives[ 0 ]);
+ }
+ else
+ {
+ my @neg_soln = (@negatives[ 0 .. 1 ], $positives[ 0 ]);
+ my @pos_soln = @positives[ 0 .. 2 ];
+ my $neg_prod = reduce_1 { $a *= $b } @neg_soln; # Product 1
+ my $pos_prod = reduce_1 { $a *= $b } @pos_soln; # Product 2
+
+ @$soln = $neg_prod > $pos_prod ? @neg_soln : @pos_soln;
+ }
+
+ return reduce_1 { $a *= $b } @$soln; # Max product
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($name, $list_str, $expected, $soln_str) = split / \| /x, $line;
+
+ for ($name, $list_str, $expected, $soln_str) # Trim whitespace
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my $solution = [];
+ my @list = split / , \s* /x, $list_str;
+ my @soln = split / , \s* /x, $soln_str;
+ my $got = find_max_product( \@list, $solution );
+
+ is $got, $expected, $name;
+ is_deeply $solution, \@soln, $name if $VERBOSE;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1 | 3, 1, 2 | 6| 1, 2, 3
+Example 2 | 4, 1, 3, 2 | 24| 2, 3, 4
+Example 3 |-1, 0, 1, 3, 1 | 3| 1, 1, 3
+Example 4 |-8, 2, -9, 0, -4, 3|216|-9, -8, 3
+Negative 1 |-2, -3, -1 | -6|-3, -2, -1
+Negative 2 |-2, -3, -1, -10, -10| -6|-3, -2, -1
+Min mixed 1|-1, 5, 2 |-10|-1, 2, 5
+Min mixed 2|-1, 5, -2 | 10|-2, -1, 5
+1 negative | 1, 2, 3, 7, -4| 42| 2, 3, 7
+2 negatives| 1, 2, 3, -7, -4| 84|-7, -4, 3
+3 negatives| 1, -2, 3, -7, -4| 84|-7, -4, 3
+Duplicates | 4, 0, 4, 5, -3| 80| 4, 4, 5
diff --git a/challenge-218/athanasius/raku/ch-1.raku b/challenge-218/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..cd766e9f2c
--- /dev/null
+++ b/challenge-218/athanasius/raku/ch-1.raku
@@ -0,0 +1,213 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 218
+=========================
+
+TASK #1
+-------
+*Maximum Product*
+
+Submitted by: Mohammad S Anwar
+
+You are given a list of 3 or more integers.
+
+Write a script to find the 3 integers whose product is the maximum and return
+it.
+
+Example 1
+
+ Input: @list = (3, 1, 2)
+ Output: 6
+
+ 1 x 2 x 3 => 6
+
+Example 2
+
+ Input: @list = (4, 1, 3, 2)
+ Output: 24
+
+ 2 x 3 x 4 => 24
+
+Example 3
+
+ Input: @list = (-1, 0, 1, 3, 1)
+ Output: 3
+
+ 1 x 1 x 3 => 3
+
+Example 4
+
+ Input: @list = (-8, 2, -9, 0, -4, 3)
+ Output: 216
+
+ -9 × -8 × 3 => 216
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2023 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. If the first integer is negative, the input list must be preceded by '--'.
+3. If VERBOSE is set to True (the default), the output is followed by an explan-
+ ation of the result.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+my Bool constant VERBOSE = True;
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 218, Task #1: Maximum Product (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ #| A list of 3 or more integers
+
+ *@list where { .all ~~ Int:D && .elems >= 3 }
+)
+#===============================================================================
+{
+ "Input: \@list = %s\n".printf: @list.join: ', ';
+
+ my Array[Int] $solution = Array[Int].new;
+ my Int $max-product = find-max-product( @list, $solution );
+
+ "Output: $max-product".put;
+
+ "\n%s = $max-product\n".printf: $solution.join: ' × ' if VERBOSE;
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub find-max-product
+(
+ List:D[Int:D] $list where { .elems >= 3 },
+ Array:D[Int:D] $solution is rw
+--> Int:D
+)
+#-------------------------------------------------------------------------------
+{
+ my Int (@neg, @pos);
+
+ ($_ < 0 ?? @neg !! @pos).push: $_ for @$list; # Partition the list
+
+ @neg.= sort; # Ascending
+ @pos.= sort: { $^b cmp $^a }; # Descending
+
+ if $list.elems == 3 || @neg.elems < 2 || @pos.elems == 0
+ {
+ $solution = Array[Int].new: $list.sort[ *-3 .. *-1 ];
+ }
+ elsif @pos.elems < 3
+ {
+ $solution = Array[Int].new: |@neg[ 0 .. 1 ], @pos[ 0 ];
+ }
+ else
+ {
+ my Int @neg-soln = Array[Int].new: |@neg[ 0 .. 1 ], @pos[ 0 ];
+ my Int @pos-soln = Array[Int].new: @pos[ 0 .. 2 ];
+ my Int $neg-prod = [*] @neg-soln; # Product 1
+ my Int $pos-prod = [*] @pos-soln; # Product 2
+
+ $solution = $neg-prod > $pos-prod ?? @neg-soln !! @pos-soln;
+ }
+
+ return [*] @$solution; # Max product
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($name, $list-str, $expected, $soln-str) = $line.split: / \| /;
+
+ for $name, $list-str, $expected, $soln-str # Trim whitespace
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Array[Int] $solution = Array[Int].new;
+
+ my Int @list = $list-str.split( / \, \s* /, :skip-empty ).map: { .Int };
+ my Int @soln = $soln-str.split( / \, \s* /, :skip-empty ).map: { .Int };
+ my Int $got = find-max-product( @list, $solution );
+
+ is $got, $expected.Int, $name;
+ is-deeply $solution, @soln, $name if VERBOSE;
+ }
+
+ done-testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error( Str:D $message )
+#-------------------------------------------------------------------------------
+{
+ "ERROR: $message".put;
+
+ USAGE();
+
+ exit 0;
+}
+
+#-------------------------------------------------------------------------------
+sub USAGE()
+#-------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s:g/ ($*PROGRAM-NAME) /raku $0/;
+
+ $usage.put;
+}
+
+#-------------------------------------------------------------------------------
+sub test-data( --> Str:D )
+#-------------------------------------------------------------------------------
+{
+ return q:to/END/;
+ Example 1 | 3, 1, 2 | 6| 1, 2, 3
+ Example 2 | 4, 1, 3, 2 | 24| 2, 3, 4
+ Example 3 |-1, 0, 1, 3, 1 | 3| 1, 1, 3
+ Example 4 |-8, 2, -9, 0, -4, 3|216|-9, -8, 3
+ Negative 1 |-2, -3, -1 | -6|-3, -2, -1
+ Negative 2 |-2, -3, -1, -10, -10| -6|-3, -2, -1
+ Min mixed 1|-1, 5, 2 |-10|-1, 2, 5
+ Min mixed 2|-1, 5, -2 | 10|-2, -1, 5
+ 1 negative | 1, 2, 3, 7, -4| 42| 2, 3, 7
+ 2 negatives| 1, 2, 3, -7, -4| 84|-7, -4, 3
+ 3 negatives| 1, -2, 3, -7, -4| 84|-7, -4, 3
+ Duplicates | 4, 0, 4, 5, -3| 80| 4, 4, 5
+ END
+}
+
+################################################################################