From cf9d6740486552969e266fa0ab4f678883d7ef9d Mon Sep 17 00:00:00 2001 From: PerlMonk-Athanasius Date: Sun, 7 May 2023 23:58:45 +1000 Subject: Perl & Raku solutions to Tasks 1 & 2 for Week 215 --- challenge-215/athanasius/perl/ch-1.pl | 215 ++++++++++++++++++++++++++++++++ challenge-215/athanasius/perl/ch-2.pl | 214 +++++++++++++++++++++++++++++++ challenge-215/athanasius/raku/ch-1.raku | 198 +++++++++++++++++++++++++++++ challenge-215/athanasius/raku/ch-2.raku | 192 ++++++++++++++++++++++++++++ 4 files changed, 819 insertions(+) create mode 100644 challenge-215/athanasius/perl/ch-1.pl create mode 100644 challenge-215/athanasius/perl/ch-2.pl create mode 100644 challenge-215/athanasius/raku/ch-1.raku create mode 100644 challenge-215/athanasius/raku/ch-2.raku diff --git a/challenge-215/athanasius/perl/ch-1.pl b/challenge-215/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..8a86b41ea9 --- /dev/null +++ b/challenge-215/athanasius/perl/ch-1.pl @@ -0,0 +1,215 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 215 +========================= + +TASK #1 +------- +*Odd One Out* + +Submitted by: Mohammad S Anwar + +You are given a list of words (alphabetic characters only) of same size. + +Write a script to remove all words not sorted alphabetically and print the +number of words in the list that are not alphabetically sorted. + +Example 1 + + Input: @words = ('abc', 'xyz', 'tsu') + Output: 1 + + The words 'abc' and 'xyz' are sorted and can't be removed. + The word 'tsu' is not sorted and hence can be removed. + +Example 2 + + Input: @words = ('rat', 'cab', 'dad') + Output: 3 + + None of the words in the given list are sorted. + Therefore all three needs to be removed. + +Example 3 + + Input: @words = ('x', 'y', 'z') + Output: 0 + +=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 a + breakdown of the sorted and unsorted words. + +Assumptions +----------- +1. "Alphabetic characters" are A-Z and a-z only. +2. "Sorted alphabetically" means sorted in monotonically ascending alphabetical + order. +3. Treatment of uppercase letters: + a. If the constant $ASCIIBETICAL is set to a true value, uppercase letters + rank below lowercase letters; so, e.g., "Bade" IS alphabetically sorted. + b. Otherwise (the default), each uppercase letter is considered identical to + its lowercase counterpart; so, "Bade" is equivalent to "bade", which is + NOT alphabetically sorted. + +=cut +#=============================================================================== + +use strict; +use warnings; +use Const::Fast; +use Test::More; + +const my $ASCIIBETICAL => 0; +const my $VERBOSE => 1; +const my $USAGE => +"Usage: + perl $0 [ ...] + perl $0 + + [ ...] Non-empty list of same-size words (chars A-Z and a-z only) +"; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 215, Task #1: Odd One Out (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + if (scalar @ARGV == 0) + { + run_tests(); + } + else + { + my $words = parse_command_line(); + + printf "Input: \@words = (%s)\n", join ', ', @$words; + + my ($sorted, $unsorted) = partition( $words ); + + print "Sorting ASCIIbetically...\n" if $ASCIIBETICAL; + + printf "Output: %d\n", scalar @$unsorted; + + if ($VERBOSE) + { + printf "\nSorted: (%s)\n", join ', ', map { qq['$_'] } @$sorted; + printf "Unsorted: (%s)\n", join ', ', map { qq['$_'] } @$unsorted; + } + } +} + +#------------------------------------------------------------------------------- +sub partition +#------------------------------------------------------------------------------- +{ + my ($words) = @_; + my (@sorted, @unsorted); + + for my $word (@$words) + { + my $sorted = 1; + my $previous = ''; + + for (split //, $word) + { + my $letter = $ASCIIBETICAL ? $_ : lc; + + if ($letter lt $previous) + { + $sorted = 0; + last; + } + + $previous = $letter; + } + + push @{ $sorted ? \@sorted : \@unsorted }, $word; + } + + return (\@sorted, \@unsorted); +} + +#------------------------------------------------------------------------------- +sub parse_command_line +#------------------------------------------------------------------------------- +{ + my $first = $ARGV[ 0 ]; + + for (@ARGV) + { + length == length $first + or error( 'The input words are not all of the same size' ); + } + + return \@ARGV; +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = ) + { + chomp $line; + + my ($test_name, $words, $expected) = split / \| /x, $line; + + SKIP: + { + skip 'This test requires $ASCIIBETICAL to be False' + if $ASCIIBETICAL && $test_name =~ / case /x; + + s/ \s+ $ //x for $test_name, $words; # Trim whitespace + + my @words = split / , /x, $words; + my ($sorted, $unsorted) = partition( \@words ); + + is scalar( @$unsorted ), $expected, $test_name; + } + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1 |abc,xyz,tsu |1 +Example 2 |rat,cab,dad |3 +Example 3 |x,y,z |0 +Repeats |beet,allow,abbot,boot,redder|1 +Capitals |ABC,XYZ,TSU |1 +Mixed case|Bade,abcd,Abcd |1 diff --git a/challenge-215/athanasius/perl/ch-2.pl b/challenge-215/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..5cf62d2090 --- /dev/null +++ b/challenge-215/athanasius/perl/ch-2.pl @@ -0,0 +1,214 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 215 +========================= + +TASK #2 +------- +*Number Placement* + +Submitted by: Mohammad S Anwar + +You are given a list of numbers having just 0 and 1. You are also given place- +ment count (>=1). + +Write a script to find out if it is possible to replace 0 with 1 in the given +list. The only condition is that you can only replace when there is no 1 on +either side. Print 1 if it is possible otherwise 0. + +Example 1: + + Input: @numbers = (1,0,0,0,1), $count = 1 + Output: 1 + + You are asked to replace only one 0 as given count is 1. + We can easily replace middle 0 in the list i.e. (1,0,1,0,1). + +Example 2: + + Input: @numbers = (1,0,0,0,1), $count = 2 + Output: 0 + + You are asked to replace two 0's as given count is 2. + It is impossible to replace two 0's. + +Example 3: + + Input: @numbers = (1,0,0,0,0,0,0,0,1), $count = 3 + Output: 1 + +=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, an output of 1 is followed by a list + containing the required replacements. + +Assumption +---------- +The task description says: + + "...replace 0 with 1 in the given list.... you can only replace when there + is no 1 on either side." + +I assume this condition (re-)applies *as each replacement is made*. For example, +in the list: + + (1,0,0,0,0,1) + a b c d e f + +the middle two zeros (c and d) both satisfy the condition at the outset; but +once a single replacement is made, e.g. replace c: (1,0,1,0,0,1), the condition +no longer holds for d, so no further replacement is possible. + +=cut +#=============================================================================== + +use strict; +use warnings; +use Const::Fast; +use Regexp::Common qw( number ); +use Test::More; + +const my $VERBOSE => 1; +const my $USAGE => +"Usage: + perl $0 + perl $0 + + Non-empty string of 0s and 1s + Placement count (>= 1)\n"; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 215, Task #2: Number Placement (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + my $args = scalar @ARGV; + + if ($args == 0) + { + run_tests(); + } + elsif ($args == 2) + { + my ($numbers, $count) = parse_command_line(); + + printf "Input: \@numbers = (%s), \$count = %d\n", + join( ',', @$numbers ), $count; + + my $new_list = make_replacements( $numbers, $count ); + + printf "Output: %d\n", defined $new_list ? 1 : 0; + + if ($VERBOSE && defined $new_list) + { + printf "\nReplacement list: (%s)\n", join ',', @$new_list; + } + } + else + { + error( "Expected 0 or 2 command-line arguments, found $args" ); + } +} + +#------------------------------------------------------------------------------- +sub make_replacements +#------------------------------------------------------------------------------- +{ + my ($numbers, $count) = @_; + + for my $i (0 .. $#$numbers) + { + if (($i == 0 || $numbers->[ $i - 1 ] == 0) && + $numbers->[ $i ] == 0 && + ($i == $#$numbers || $numbers->[ $i + 1 ] == 0)) + { + $numbers->[ $i ] = 1; + + return $numbers if --$count == 0; + } + } + + return; +} + +#------------------------------------------------------------------------------- +sub parse_command_line +#------------------------------------------------------------------------------- +{ + my ($numbers, $count) = @ARGV; + + $numbers =~ / ^ [01]+ $ /x + or error( qq[Invalid argument "$numbers"] ); + + my @numbers = split //, $numbers; + + $count =~ / ^ $RE{num}{int} $ /x && $count > 0 + or error( qq[Invalid argument "$count"] ); + + return (\@numbers, $count); +} + +#------------------------------------------------------------------------------- +sub run_tests +#------------------------------------------------------------------------------- +{ + print "Running the test suite\n"; + + while (my $line = ) + { + chomp $line; + + my ($test_name, $numbers, $count, $expected) = split / \| /x, $line; + + s/ \s+ $ //x for $test_name, $numbers, $count; # Trim whitespace + + my @numbers = split //, $numbers; + my $new_list = make_replacements( \@numbers, $count ); + my $got = defined $new_list ? 1 : 0; + + is $got, $expected, $test_name; + } + + done_testing; +} + +#------------------------------------------------------------------------------- +sub error +#------------------------------------------------------------------------------- +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +################################################################################ + +__DATA__ +Example 1 |10001 |1|1 +Example 2 |10001 |2|0 +Example 3 |100000001 |3|1 +Beginning |0010000000010|4|1 +Ending |0100000000100|4|1 +Start and end|001100 |2|1 +One too many |0100000000100|5|0 diff --git a/challenge-215/athanasius/raku/ch-1.raku b/challenge-215/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..15597de134 --- /dev/null +++ b/challenge-215/athanasius/raku/ch-1.raku @@ -0,0 +1,198 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 215 +========================= + +TASK #1 +------- +*Odd One Out* + +Submitted by: Mohammad S Anwar + +You are given a list of words (alphabetic characters only) of same size. + +Write a script to remove all words not sorted alphabetically and print the +number of words in the list that are not alphabetically sorted. + +Example 1 + + Input: @words = ('abc', 'xyz', 'tsu') + Output: 1 + + The words 'abc' and 'xyz' are sorted and can't be removed. + The word 'tsu' is not sorted and hence can be removed. + +Example 2 + + Input: @words = ('rat', 'cab', 'dad') + Output: 3 + + None of the words in the given list are sorted. + Therefore all three needs to be removed. + +Example 3 + + Input: @words = ('x', 'y', 'z') + Output: 0 + +=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 VERBOSE is set to True (the default), the output is followed by a break- + down of the sorted and unsorted words. + +Assumptions +----------- +1. "Alphabetic characters" are A-Z and a-z only. +2. "Sorted alphabetically" means sorted in monotonically ascending alphabetical + order. +3. Treatment of uppercase letters: + a. If the constant "ASCIIBETICAL" is set to True, uppercase letters rank + below lowercase letters; so, e.g., "Bade" IS alphabetically sorted. + b. Otherwise (the default), each uppercase letter is considered identical to + its lowercase counterpart; so, "Bade" is equivalent to "bade", which is + NOT alphabetically sorted. + +=end comment +#=============================================================================== + +use Test; + +my Bool constant ASCIIBETICAL = False; +my Bool constant VERBOSE = True; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 215, Task #1: Odd One Out (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + #| Non-empty list of same-size words (chars A-Z and a-z only) + + *@words where { .elems > 0 && @words[ 0 ].chars == .all.chars } +) +#=============================================================================== +{ + "Input: \@words = (%s)\n".printf: @words.map( { qq['$_'] } ).join: ', '; + + my Array[Str] ($sorted, $unsorted) = partition( @words ); + + 'Sorting ASCIIbetically...'.put if ASCIIBETICAL; + + "Output: %d\n"\.printf: $unsorted.elems; + + if VERBOSE + { + "\nSorted: (%s)\n".printf: $sorted.map( { qq['$_'] } ).join: ', '; + "Unsorted: (%s)\n".printf: $unsorted.map( { qq['$_'] } ).join: ', '; + } +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub partition( Array:D[Str:D] $words --> List:D[List:D[Str:D], List:D[Str:D]] ) +#------------------------------------------------------------------------------- +{ + my Str (@sorted, @unsorted); + + for @$words -> Str $word + { + my Bool $sorted = True; + my Str $previous = ''; + + for $word.split: '', :skip-empty + { + my Str $letter = ASCIIBETICAL ?? $_ !! .lc; + + if $letter lt $previous + { + $sorted = False; + last; + } + + $previous = $letter; + } + + ($sorted ?? @sorted !! @unsorted).push: $word; + } + + return @sorted, @unsorted; +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $words, $expected) = $line.split: / \| /; + + if ASCIIBETICAL && $test-name ~~ / case / + { + skip 'This test requires "ASCIIBETICAL" to be False'; + } + else + { + s/ \s+ $ // for $test-name, $words; # Trim whitespace + + my Str @words = $words.split: ','; + my Array[Str] ($sorted, $unsorted) = partition( @words ); + + is $unsorted.elems, $expected.Int, $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 |abc,xyz,tsu |1 + Example 2 |rat,cab,dad |3 + Example 3 |x,y,z |0 + Repeats |beet,allow,abbot,boot,redder|1 + Capitals |ABC,XYZ,TSU |1 + Mixed case|Bade,abcd,Abcd |1 + END +} + +################################################################################ diff --git a/challenge-215/athanasius/raku/ch-2.raku b/challenge-215/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..77b90c8543 --- /dev/null +++ b/challenge-215/athanasius/raku/ch-2.raku @@ -0,0 +1,192 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 215 +========================= + +TASK #2 +------- +*Number Placement* + +Submitted by: Mohammad S Anwar + +You are given a list of numbers having just 0 and 1. You are also given place- +ment count (>=1). + +Write a script to find out if it is possible to replace 0 with 1 in the given +list. The only condition is that you can only replace when there is no 1 on +either side. Print 1 if it is possible otherwise 0. + +Example 1: + + Input: @numbers = (1,0,0,0,1), $count = 1 + Output: 1 + + You are asked to replace only one 0 as given count is 1. + We can easily replace middle 0 in the list i.e. (1,0,1,0,1). + +Example 2: + + Input: @numbers = (1,0,0,0,1), $count = 2 + Output: 0 + + You are asked to replace two 0's as given count is 2. + It is impossible to replace two 0's. + +Example 3: + + Input: @numbers = (1,0,0,0,0,0,0,0,1), $count = 3 + Output: 1 + +=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 $VERBOSE is set to True, an output of 1 is followed by a list containing + the required replacements. + +Assumption +---------- +The task description says: + + "...replace 0 with 1 in the given list.... you can only replace when there + is no 1 on either side." + +I assume this condition (re-)applies *as each replacement is made*. For example, +in the list: + + (1,0,0,0,0,1) + a b c d e f + +the middle two zeros (c and d) both satisfy the condition at the outset; but +once a single replacement is made, e.g. replace c: (1,0,1,0,0,1), the condition +no longer holds for d, so no further replacement is possible. + +=end comment +#=============================================================================== + +my Bool constant $VERBOSE = True; + +subset Pos of Int where * > 0; +subset Bit of Int where 0|1; + +use Test; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 215, Task #2: Number Placement (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + Str:D $numbers where / ^ <[ 0 1 ]>+ $ /, #= Non-empty string of 0s and 1s + Pos:D $count #= Placement count (>= 1) +) +#=============================================================================== +{ + my Bit @numbers = $numbers.split( '', :skip-empty ).map: { .Int }; + + "Input: \@numbers = (%s), \$count = %d\n".printf: + @numbers.join( ',' ), $count; + + my Bit @new-list = make-replacements( @numbers, $count ); + + "Output: %d\n".printf: @new-list.elems ?? 1 !! 0; + + if ($VERBOSE && @new-list.elems) + { + printf "\nReplacement list: (%s)\n", join ',', @new-list; + } +} + +#=============================================================================== +multi sub MAIN() # No input: run the test suite +#=============================================================================== +{ + run-tests(); +} + +#------------------------------------------------------------------------------- +sub make-replacements( Array:D[Bit:D] $numbers, Pos:D $count --> List:D[Bit:D] ) +#------------------------------------------------------------------------------- +{ + my UInt $my-count = $count; + my Bit @my-numbers = |$numbers; + + for 0 .. $numbers.end -> UInt $i + { + if ($i == 0 || @my-numbers[ $i - 1 ] == 0) && + @my-numbers[ $i ] == 0 && + ($i == $numbers.end || @my-numbers[ $i + 1 ] == 0) + { + @my-numbers[ $i ] = 1; + + return @my-numbers if --$my-count == 0; + } + } + + return (); # Empty list +} + +#------------------------------------------------------------------------------- +sub run-tests() +#------------------------------------------------------------------------------- +{ + 'Running the test suite'.put; + + for test-data.lines -> Str $line + { + my Str ($test-name, $numbers, $count, $expected) = $line.split: / \| /; + + s/ \s+ $ // for $test-name, $numbers, $count; # Trim whitespace + + my Bit @numbers = $numbers.split( '', :skip-empty ).map: { .Int }; + my Bit @new-list = make-replacements( @numbers, $count.Int ); + + is @new-list.elems ?? 1 !! 0, $expected.Int, $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 |10001 |1|1 + Example 2 |10001 |2|0 + Example 3 |100000001 |3|1 + Beginning |0010000000010|4|1 + Ending |0100000000100|4|1 + Start and end|001100 |2|1 + One too many |0100000000100|5|0 + END +} + +################################################################################ -- cgit