diff options
| author | Mohammad Sajid Anwar <Mohammad.Anwar@yahoo.com> | 2025-11-02 13:47:46 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-02 13:47:46 +0000 |
| commit | aebcea5d241033175603fd39a3250d86146b5536 (patch) | |
| tree | 50062eeb8ae0abce416a1f111ed8ba6186316007 | |
| parent | 9490291b8cf7bf2c950c7ffe532f220cc55a111c (diff) | |
| parent | aa728e77bec9b3cedf43273a8904c9b1bd38d1fa (diff) | |
| download | perlweeklychallenge-club-aebcea5d241033175603fd39a3250d86146b5536.tar.gz perlweeklychallenge-club-aebcea5d241033175603fd39a3250d86146b5536.tar.bz2 perlweeklychallenge-club-aebcea5d241033175603fd39a3250d86146b5536.zip | |
Merge pull request #12959 from PerlMonk-Athanasius/branch-for-challenge-345
Perl & Raku solutions to Tasks 1 & 2 for Week 345
| -rw-r--r-- | challenge-345/athanasius/perl/ch-1.pl | 193 | ||||
| -rw-r--r-- | challenge-345/athanasius/perl/ch-2.pl | 213 | ||||
| -rw-r--r-- | challenge-345/athanasius/raku/ch-1.raku | 183 | ||||
| -rw-r--r-- | challenge-345/athanasius/raku/ch-2.raku | 207 |
4 files changed, 796 insertions, 0 deletions
diff --git a/challenge-345/athanasius/perl/ch-1.pl b/challenge-345/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..50c8526059 --- /dev/null +++ b/challenge-345/athanasius/perl/ch-1.pl @@ -0,0 +1,193 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 345 +========================= + +TASK #1 +------- +*Peak Positions* + +Submitted by: Mohammad Sajid Anwar + +You are given an array of integers, @ints. + +Find all the peaks in the array, a peak is an element that is strictly greater +than its left and right neighbours. Return the indices of all such peak posi- +tions. + +Example 1 + + Input: @ints = (1, 3, 2) + Output: (1) + +Example 2 + + Input: @ints = (2, 4, 6, 5, 3) + Output: (2) + +Example 3 + + Input: @ints = (1, 2, 3, 2, 4, 1) + Output: (2, 4) + +Example 4 + + Input: @ints = (5, 3, 1) + Output: (0) + +Example 5 + + Input: @ints = (1, 5, 1, 5, 1, 5, 1) + Output: (1, 3, 5) + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2025 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Assumption +---------- +From Example 4, it appears that an integer in either end position is considered +to be "strictly greater than" its non-existent neighbour. + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. A non-empty list of integers is entered on the command-line. + +=cut +#=============================================================================== + +use v5.38.2; # Enables strictures +use warnings; +use Const::Fast; +use Regexp::Common qw( number ); +use Test::More; + +const my $USAGE => <<END; +Usage: + perl $0 [<ints> ...] + perl $0 + + [<ints> ...] A non-empty list of integers +END +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 345, Task #1: Peak Positions (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + if (scalar @ARGV == 0) + { + run_tests(); + } + else + { + my @ints = @ARGV; + + / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] ) + for @ints; + + printf "Input: \@ints = (%s)\n", join ', ', @ints; + + my $peaks = find_peaks( \@ints ); + + printf "Output: (%s)\n", join ', ', @$peaks; + } +} + +#------------------------------------------------------------------------------- +sub find_peaks +#------------------------------------------------------------------------------- +{ + my ($ints) = @_; + my $size = scalar @$ints; + my @peaks; + + if ($size == 0) + { + die 'Empty integer list'; + } + elsif ($size == 1) + { + push @peaks, 0; + } + else + { + my $e = $#$ints; + + push @peaks, 0 if $ints->[0] > $ints->[1]; + + for my $i (1 .. $e - 1) + { + push @peaks, $i if $ints->[$i] > $ints->[$i - 1] && + $ints->[$i] > $ints->[$i + 1] + } + + push @peaks, $e if $ints->[$e] > $ints->[$e - 1]; + } + + return \@peaks; +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = <DATA>) + { + chomp $line; + + my ($test_name, $ints_str, $expd_str) = split / \| /x, $line; + + for ($test_name, $ints_str, $expd_str) + { + s/ ^ \s+ //x; + s/ \s+ $ //x; + } + + my @ints = split / \s+ /x, $ints_str; + my $peaks = find_peaks( \@ints ); + my @expd = split / \s+ /x, $expd_str; + + is_deeply $peaks, \@expd, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1|1 3 2 |1 +Example 2|2 4 6 5 3 |2 +Example 3|1 2 3 2 4 1 |2 4 +Example 4|5 3 1 |0 +Example 5|1 5 1 5 1 5 1|1 3 5 +Singleton|4 |0 +Negatives|0 -1 1 -2 0 |0 2 4 diff --git a/challenge-345/athanasius/perl/ch-2.pl b/challenge-345/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..03f48be9ab --- /dev/null +++ b/challenge-345/athanasius/perl/ch-2.pl @@ -0,0 +1,213 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 345 +========================= + +TASK #2 +------- +*Last Visitor* + +Submitted by: Mohammad Sajid Anwar + +You are given an integer array @ints where each element is either a positive +integer or -1. + +We process the array from left to right while maintaining two lists: + + @seen: stores previously seen positive integers (newest at the front) + @ans: stores the answers for each -1 + +Rules: + + If $ints[i] is a positive number -> insert it at the front of @seen + If $ints[i] is -1: + +Let $x be how many -1s in a row we've seen before this one. + +If $x < len(@seen) -> append seen[x] to @ans + +Else -> append -1 to @ans + +At the end, return @ans. + +Example 1 + + Input: @ints = (5, -1, -1) + Output: (5, -1) + + @seen = (5) + First -1: @ans = (5) + Second -1: @ans = (5, -1) + +Example 2 + + Input: @ints = (3, 7, -1, -1, -1) + Output: (7, 3, -1) + + @seen = (3, 7) + First -1: @ans = (7) + Second -1: @ans = (7, 3) + Third -1: @ans = (7, 3, -1) + +Example 3 + + Input: @ints = (2, -1, 4, -1, -1) + Output: (2, 4, 2) + +Example 4 + + Input: @ints = (10, 20, -1, 30, -1, -1) + Output: (20, 30, 20) + +Example 5 + + Input: @ints = (-1, -1, 5, -1) + Output: (-1, -1, 5) + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2025 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=comment + +Assumptions +----------- +1. A "positive integer" is any non-negative integer (i.e., including zero). + Hence, the allowed values in @ints are integers in the range: -1, 0, 1, ... +2. "Let $x be how many -1s in a row we've seen before this one." should read: + "Let $x be how many -1s in a row we've seen *immediately* before this one." + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. A non-empty list of integers is entered on the command-line; each integer is + greater than or equal to -1. + +=cut +#=============================================================================== + +use v5.38.2; # Enables strictures +use warnings; +use Const::Fast; +use Regexp::Common qw( number ); +use Test::More; + +const my $USAGE => <<END; +Usage: + perl $0 [<ints> ...] + perl $0 + + [<ints> ...] A non-empty list of integers, each -1 or greater +END + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 345, Task #2: Last Visitor (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + if (scalar @ARGV == 0) + { + run_tests(); + } + else + { + my @ints = @ARGV; + + for (@ints) + { + / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] ); + $_ >= -1 or error( qq["$_" is too small]); + } + + printf "Input: \@ints = (%s)\n", join ', ', @ints; + + my $ans = find_answers( \@ints ); + + printf "Output: (%s)\n", join ', ', @$ans; + } +} + +#------------------------------------------------------------------------------- +sub find_answers +#------------------------------------------------------------------------------- +{ + my ($ints) = @_; + my (@seen, @ans); + my $x = 0; + + for my $n (@$ints) + { + if ($n >= 0) + { + unshift @seen, $n; + $x = 0; + } + else + { + push @ans, (scalar @seen > $x) ? $seen[$x] : -1; + ++$x; + } + } + + return \@ans; +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = <DATA>) + { + chomp $line; + + my ($test_name, $ints_str, $expd_str) = split / \| /x, $line; + + for ($test_name, $ints_str, $expd_str) + { + s/ ^ \s+ //x; + s/ \s+ $ //x; + } + + my @ints = split / \s+ /x, $ints_str; + my $ans = find_answers( \@ints ); + my @expd = split / \s+ /x, $expd_str; + + is_deeply $ans, \@expd, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1| 5 -1 -1 | 5 -1 +Example 2| 3 7 -1 -1 -1 | 7 3 -1 +Example 3| 2 -1 4 -1 -1 | 2 4 2 +Example 4|10 20 -1 30 -1 -1|20 30 20 +Example 5|-1 -1 5 -1 |-1 -1 5 diff --git a/challenge-345/athanasius/raku/ch-1.raku b/challenge-345/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..9668f9c78a --- /dev/null +++ b/challenge-345/athanasius/raku/ch-1.raku @@ -0,0 +1,183 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 345 +========================= + +TASK #1 +------- +*Peak Positions* + +Submitted by: Mohammad Sajid Anwar + +You are given an array of integers, @ints. + +Find all the peaks in the array, a peak is an element that is strictly greater +than its left and right neighbours. Return the indices of all such peak posi- +tions. + +Example 1 + + Input: @ints = (1, 3, 2) + Output: (1) + +Example 2 + + Input: @ints = (2, 4, 6, 5, 3) + Output: (2) + +Example 3 + + Input: @ints = (1, 2, 3, 2, 4, 1) + Output: (2, 4) + +Example 4 + + Input: @ints = (5, 3, 1) + Output: (0) + +Example 5 + + Input: @ints = (1, 5, 1, 5, 1, 5, 1) + Output: (1, 3, 5) + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2025 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=begin comment + +Assumption +---------- +From Example 4, it appears that an integer in either end position is considered +to be "strictly greater than" its non-existent neighbour. + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. A non-empty list of integers is entered on the command-line. +3. If the first integer is negative, it must be preceded by "--" to signal that + it is not a command-line flag. + +=end comment +#=============================================================================== + +use Test; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 345, Task #1: Peak Positions (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + #| A non-empty list of integers + + *@ints where { .elems > 0 && .all ~~ Int:D } +) +#=============================================================================== +{ + "Input: \@ints = (%s)\n".printf: @ints.join: ', '; + + my UInt @peaks = find-peaks( @ints ); + + "Output: (%s)\n".printf: @peaks.join: ', '; +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub find-peaks( List:D[Int:D] $ints where { .elems > 0 } --> List:D[UInt:D] ) +#------------------------------------------------------------------------------- +{ + my UInt @peaks; + + if $ints.elems == 1 + { + @peaks.push: 0; + } + else + { + my UInt $e = $ints.end; + + @peaks.push: 0 if $ints[0] > $ints[1]; + + for 1 .. $e - 1 -> UInt $i + { + @peaks.push: $i if $ints[$i] > $ints[$i - 1] && + $ints[$i] > $ints[$i + 1] + } + + @peaks.push: $e if $ints[$e] > $ints[$e - 1]; + } + + return @peaks; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $ints-str, $expd-str) = $line.split: / \| /; + + for $test-name, $ints-str, $expd-str + { + s/ ^ \s+ //; + s/ \s+ $ //; + } + + my Int @ints = $ints-str.split( / \s+ /, :skip-empty ).map: { .Int }; + my UInt @peaks = find-peaks( @ints ); + my UInt @expd = $expd-str.split( / \s+ /, :skip-empty ).map: { .Int }; + + is-deeply @peaks, @expd, $test-name; + } + + done-testing; +} + +#------------------------------------------------------------------------------- +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|1 3 2 |1 + Example 2|2 4 6 5 3 |2 + Example 3|1 2 3 2 4 1 |2 4 + Example 4|5 3 1 |0 + Example 5|1 5 1 5 1 5 1|1 3 5 + Singleton|4 |0 + Negatives|0 -1 1 -2 0 |0 2 4 + END +} + +################################################################################ diff --git a/challenge-345/athanasius/raku/ch-2.raku b/challenge-345/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..e1ad9a7797 --- /dev/null +++ b/challenge-345/athanasius/raku/ch-2.raku @@ -0,0 +1,207 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 345 +========================= + +TASK #2 +------- +*Last Visitor* + +Submitted by: Mohammad Sajid Anwar + +You are given an integer array @ints where each element is either a positive +integer or -1. + +We process the array from left to right while maintaining two lists: + + @seen: stores previously seen positive integers (newest at the front) + @ans: stores the answers for each -1 + +Rules: + + If $ints[i] is a positive number -> insert it at the front of @seen + If $ints[i] is -1: + +Let $x be how many -1s in a row we've seen before this one. + +If $x < len(@seen) -> append seen[x] to @ans + +Else -> append -1 to @ans + +At the end, return @ans. + +Example 1 + + Input: @ints = (5, -1, -1) + Output: (5, -1) + + @seen = (5) + First -1: @ans = (5) + Second -1: @ans = (5, -1) + +Example 2 + + Input: @ints = (3, 7, -1, -1, -1) + Output: (7, 3, -1) + + @seen = (3, 7) + First -1: @ans = (7) + Second -1: @ans = (7, 3) + Third -1: @ans = (7, 3, -1) + +Example 3 + + Input: @ints = (2, -1, 4, -1, -1) + Output: (2, 4, 2) + +Example 4 + + Input: @ints = (10, 20, -1, 30, -1, -1) + Output: (20, 30, 20) + +Example 5 + + Input: @ints = (-1, -1, 5, -1) + Output: (-1, -1, 5) + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2025 PerlMonk Athanasius # +#--------------------------------------# + +#=============================================================================== +=begin comment + +Assumptions +----------- +1. A "positive integer" is any non-negative integer (i.e., including zero). + Hence, the allowed values in @ints are integers in the range: -1, 0, 1, ... +2. "Let $x be how many -1s in a row we've seen before this one." should read: + "Let $x be how many -1s in a row we've seen *immediately* before this one." + +Interface +--------- +1. If no command-line arguments are given, the test suite is run. Otherwise: +2. A non-empty list of integers is entered on the command-line; each integer is + greater than or equal to -1. +3. If the first such integer is -1, it must be preceded by "--" to signal that + it is not a command-line flag. + +=end comment +#=============================================================================== + +use Test; + +subset Type of Int where * >= -1; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 345, Task #2: Last Visitor (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + #| A non-empty list of integers, each -1 or greater + + *@ints where { .elems > 0 && .all ~~ Type:D } +) +#=============================================================================== +{ + "Input: \@ints = (%s)\n".printf: @ints.join: ', '; + + my Type @ans = find-answers( @ints ); + + "Output: (%s)\n".printf: @ans.join: ', '; +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub find-answers( List:D[Type:D] $ints --> List:D[Type:D] ) +#------------------------------------------------------------------------------- +{ + my UInt @seen; + my Type @ans; + my UInt $x = 0; + + for @$ints -> Type $n + { + if $n >= 0 + { + @seen.unshift: $n; + $x = 0; + } + else + { + @ans.push: (@seen.elems > $x) ?? @seen[$x] !! -1; + ++$x; + } + } + + return @ans; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $ints-str, $expd-str) = $line.split: / \| /; + + for $test-name, $ints-str, $expd-str + { + s/ ^ \s+ //; + s/ \s+ $ //; + } + + my Type @ints = $ints-str.split( / \s+ /, :skip-empty ).map: { .Int }; + my Type @ans = find-answers( @ints ); + my Type @expd = $expd-str.split( / \s+ /, :skip-empty ).map: { .Int }; + + is-deeply @ans, @expd, $test-name; + } + + done-testing; +} + +#------------------------------------------------------------------------------- +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| 5 -1 -1 | 5 -1 + Example 2| 3 7 -1 -1 -1 | 7 3 -1 + Example 3| 2 -1 4 -1 -1 | 2 4 2 + Example 4|10 20 -1 30 -1 -1|20 30 20 + Example 5|-1 -1 5 -1 |-1 -1 5 + END +} + +################################################################################ |
