diff options
| author | PerlMonk Athanasius <PerlMonk.Athanasius@gmail.com> | 2021-04-04 23:16:31 +1000 |
|---|---|---|
| committer | PerlMonk Athanasius <PerlMonk.Athanasius@gmail.com> | 2021-04-04 23:16:31 +1000 |
| commit | ae2c69e4bca63b7311e2c1eafdf2dc9cb1a36889 (patch) | |
| tree | c68907212c77bbb11314f19a10be9fad97c737a7 /challenge-106/athanasius | |
| parent | d5ff10db3ea3e9e6e150316af0d4e4c0e2a37fe1 (diff) | |
| download | perlweeklychallenge-club-ae2c69e4bca63b7311e2c1eafdf2dc9cb1a36889.tar.gz perlweeklychallenge-club-ae2c69e4bca63b7311e2c1eafdf2dc9cb1a36889.tar.bz2 perlweeklychallenge-club-ae2c69e4bca63b7311e2c1eafdf2dc9cb1a36889.zip | |
Perl & Raku solutions to Tasks 1 & 2 of the Perl Weekly Challenge #106
On branch branch-for-challenge-106
Changes to be committed:
new file: challenge-106/athanasius/perl/ch-1.pl
new file: challenge-106/athanasius/perl/ch-2.pl
new file: challenge-106/athanasius/raku/ch-1.raku
new file: challenge-106/athanasius/raku/ch-2.raku
Diffstat (limited to 'challenge-106/athanasius')
| -rw-r--r-- | challenge-106/athanasius/perl/ch-1.pl | 144 | ||||
| -rw-r--r-- | challenge-106/athanasius/perl/ch-2.pl | 157 | ||||
| -rw-r--r-- | challenge-106/athanasius/raku/ch-1.raku | 106 | ||||
| -rw-r--r-- | challenge-106/athanasius/raku/ch-2.raku | 83 |
4 files changed, 490 insertions, 0 deletions
diff --git a/challenge-106/athanasius/perl/ch-1.pl b/challenge-106/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..a4015e4dd0 --- /dev/null +++ b/challenge-106/athanasius/perl/ch-1.pl @@ -0,0 +1,144 @@ +#!perl + +############################################################################### +=comment + +Perl Weekly Challenge 106 +========================= + +Task #1 +------- +*Maximum Gap* + +Submitted by: Mohammad S Anwar + +You are given an array of integers @N. + +Write a script to display the maximum difference between two successive +elements once the array is sorted. + +If the array contains only 1 element then display 0. + +Example + + Input: @N = (2, 9, 3, 5) + Output: 4 + + Input: @N = (1, 3, 8, 2, 0) + Output: 5 + + Input: @N = (5) + Output: 0 + +=cut +############################################################################### + +#--------------------------------------# +# Copyright © 2021 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=comment + +Algorithm +--------- +Straightforward: Sort the array in ascending order, then iterate through all +pairs of adjacent elements and record the largest gap. Default to zero if there +is only one element in the array. + +Note on Duplicates +------------------ +For large arrays containing many duplicate elements, it might be possible to +obtain a small speed-up by removing duplicates from the sorted array before +testing adjacent elements. The (optional) flag --u[nique] (or just -u) is +provided for this purpose. It defaults to a false value, since for small arrays +the speed-up is likely more than outweighed by the overhead of duplicate +removal. + +Command Line Usage +------------------ +Note that if the --unique flag is given and the list contains negative numbers, +the list must be preceded by -- to prevent the minus sign from being interpret- +ed as introducing a command line option. + +=cut +#============================================================================== + +use strict; +use warnings; +use Const::Fast; +use Getopt::Long; +use List::Util qw( uniqint ); +use Regexp::Common qw( number ); + +const my $USAGE => +"Usage: + perl $0 [--unique --] [<N> ...] + + --unique (Optional) remove duplicates + [<N> ...] A non-empty, unsorted list of integers\n"; + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + $| = 1; + print "\nChallenge 106, Task #1: Maximum Gap (Perl)\n\n"; +} + +#============================================================================== +MAIN: +#============================================================================== +{ + my ($unique, $N) = parse_command_line(); + + printf "Input: \@N = (%s)\n", join ', ', @$N; + + my @sorted = sort { $a <=> $b } @$N; # Sort ascending + @sorted = uniqint @sorted if $unique; # Optionally remove dups + my $max_gap = 0; + + # Test every difference between successive elements in the sorted array + + for my $i (1 .. $#sorted) + { + my $diff = $sorted[ $i ] - $sorted[ $i - 1 ]; + + $max_gap = $diff if $diff > $max_gap; + } + + print "Output: $max_gap\n"; +} + +#------------------------------------------------------------------------------ +sub parse_command_line +#------------------------------------------------------------------------------ +{ + my $unique = 0; + + GetOptions( 'unique' => \$unique ) + or error( 'Unrecognized command line argument' ); + + my @N = @ARGV; + + scalar @N > 0 or error( 'Empty list' ); + + for my $n (@N) + { + $n =~ / ^ $RE{num}{int} $ /x + or error( qq[List element "$n" is not an integer] ); + } + + return ($unique, \@N); +} + +#------------------------------------------------------------------------------ +sub error +#------------------------------------------------------------------------------ +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +############################################################################### diff --git a/challenge-106/athanasius/perl/ch-2.pl b/challenge-106/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..4c3c538170 --- /dev/null +++ b/challenge-106/athanasius/perl/ch-2.pl @@ -0,0 +1,157 @@ +#!perl + +############################################################################### +=comment + +Perl Weekly Challenge 106 +========================= + +*Decimal String* + +Submitted by: Mohammad S Anwar + +You are given numerator and denominator i.e. $N and $D. + +Write a script to convert the fraction into decimal string. If the fractional +part is recurring then put it in parenthesis. + +Example + + Input: $N = 1, $D = 3 + Output: "0.(3)" + + Input: $N = 1, $D = 2 + Output: "0.5" + + Input: $N = 5, $D = 66 + Output: "0.0(75)" + +=cut +############################################################################### + +#--------------------------------------# +# Copyright © 2021 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=comment + +Algorithm +--------- +Neither Perl itself nor the core Math::BigRat module have an equivalent to the +Raku base-repeating() method. Nor, to my surprise, am I able to find a CPAN +module which provides this functionality. So I ported to Perl the Raku source +code in rakudo-2021.03/src/core.c/Rational.pm6. + +=cut +#============================================================================== + +use strict; +use warnings; +use Const::Fast; +use List::MoreUtils qw( first_index ); +use Math::BigRat; +use POSIX qw( floor ); +use Regexp::Common qw( number ); + +const my $BASE => 10; +const my $USAGE => +"Usage: + perl $0 <N> <D> + + <N> Numerator (an integer) + <D> Denominator (a non-zero integer)\n"; + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + $| = 1; + print "\nChallenge 106, Task #2: Decimal String (Perl)\n\n"; +} + +#============================================================================== +MAIN: +#============================================================================== +{ + my ($N, $D) = parse_command_line(); + + print "Input: \$N = $N, \$D = $D\n"; + + my ($non_rep, $rep) = base_repeating( $N, $D, $BASE ); + + printf qq[Output: "%s%s"\n], $non_rep, $rep ? "($rep)" : ''; +} + +#------------------------------------------------------------------------------ +# Adapted from rakudo-2021.03/src/core.c/Rational.pm6 +# +sub base_repeating +#------------------------------------------------------------------------------ +{ + my ($N, $D, $base) = @_; + + return (0, '') if $N == 0; + + # Reduce to normal (i.e., shortest) form + + my $rat = Math::BigRat->new( "$N / $D" ); # Implicitly calls $rat->bnorm() + my $num = abs $rat->numerator; + my $den = abs $rat->denominator; + + return ('-' x ($rat < 0) . $num, '') if $den == 1; + + my @quotients = floor( $num / $den ); + my (@remainders, %remainders); + + while (1) + { + push @remainders, $num %= $den; + last if $remainders{ $num }++ || $num == 0; + $num *= $base; + push @quotients, floor( $num / $den ); + } + + my ($rep, @cycle) = ('', ()); + + if ($num) + { + @cycle = splice @quotients, 1 + first_index { $_ == $num } @remainders; + $rep = join '', @cycle; + } + + splice @quotients, 1, 0, '.'; + + my $non_rep = '-' x ($rat < 0) . join '', @quotients; + + return ($non_rep, $rep); +} + +#------------------------------------------------------------------------------ +sub parse_command_line +#------------------------------------------------------------------------------ +{ + my $c = scalar @ARGV; + $c == 2 or error( "Expected 2 command line arguments, found $c" ); + + for (@ARGV) + { + / ^ $RE{num}{int} $ /x + or error( qq["$_" is not an integer] ); + } + + $ARGV[ 1 ] == 0 and error( 'The denominator cannot be zero' ); + + return @ARGV; +} + +#------------------------------------------------------------------------------ +sub error +#------------------------------------------------------------------------------ +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +############################################################################### diff --git a/challenge-106/athanasius/raku/ch-1.raku b/challenge-106/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..5b267c60f6 --- /dev/null +++ b/challenge-106/athanasius/raku/ch-1.raku @@ -0,0 +1,106 @@ +use v6d; + +############################################################################### +=begin comment + +Perl Weekly Challenge 106 +========================= + +Task #1 +------- +*Maximum Gap* + +Submitted by: Mohammad S Anwar + +You are given an array of integers @N. + +Write a script to display the maximum difference between two successive +elements once the array is sorted. + +If the array contains only 1 element then display 0. + +Example + + Input: @N = (2, 9, 3, 5) + Output: 4 + + Input: @N = (1, 3, 8, 2, 0) + Output: 5 + + Input: @N = (5) + Output: 0 + +=end comment +############################################################################### + +#--------------------------------------# +# Copyright © 2021 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=begin comment + +Algorithm +--------- +Straightforward: Sort the array in ascending order, then iterate through all +pairs of adjacent elements and record the largest gap. Default to zero if there +is only one element in the array. + +Note on Duplicates +------------------ +For large arrays containing many duplicate elements, it might be possible to +obtain a small speed-up by removing duplicates from the sorted array before +testing adjacent elements. The (optional) flag --unique is provided for this +purpose. It defaults to False, since for small arrays the speed-up is likely +more than outweighed by the overhead of duplicate removal. + +=end comment +#============================================================================== + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + "\nChallenge 106, Task #1: Maximum Gap (Raku)\n".put; +} + +#============================================================================== +sub MAIN +( + Bool:D :$unique = False, #= (Optional) remove duplicates + *@N where { @N.elems > 0 && #= A non-empty, unsorted list + .all ~~ Int:D } #= of integers +) +#============================================================================== +{ + my Int @array = @N.map: { .Int }; # Change IntStrs to Ints + + "Input: \@N = (%s)\n".printf: @array.join: ', '; + + my Int @sorted = @array.sort; # Sort ascending + @sorted = @sorted.squish if $unique; # Optionally remove dups + my UInt $max-gap = 0; + + # Test every difference between successive elements in the sorted array + + for 1 .. @sorted.end -> UInt $i + { + my UInt $diff = @sorted[ $i ] - @sorted[ $i - 1 ]; + + $max-gap = $diff if $diff > $max-gap; + } + + "Output: $max-gap".put; +} + +#------------------------------------------------------------------------------ +sub USAGE() +#------------------------------------------------------------------------------ +{ + my Str $usage = $*USAGE; + + $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/; + $usage.put; +} + +############################################################################## diff --git a/challenge-106/athanasius/raku/ch-2.raku b/challenge-106/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..c5b31e4c50 --- /dev/null +++ b/challenge-106/athanasius/raku/ch-2.raku @@ -0,0 +1,83 @@ +use v6d; + +############################################################################### +=begin comment + +Perl Weekly Challenge 106 +========================= + +*Decimal String* + +Submitted by: Mohammad S Anwar + +You are given numerator and denominator i.e. $N and $D. + +Write a script to convert the fraction into decimal string. If the fractional +part is recurring then put it in parenthesis. + +Example + + Input: $N = 1, $D = 3 + Output: "0.(3)" + + Input: $N = 1, $D = 2 + Output: "0.5" + + Input: $N = 5, $D = 66 + Output: "0.0(75)" + +=end comment +############################################################################### + +#--------------------------------------# +# Copyright © 2021 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=begin comment + +Algorithm +--------- +The Raku Rat class (which does the Rational role) has a mathod base-repeating() +which returns, separately, the non-repeating part and the repeating part (if +any) of a given rational number. + +=end comment +#============================================================================== + +my UInt constant $BASE = 10; + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + "\nChallenge 106, Task #2: Decimal String (Raku)\n".put; +} + +#============================================================================== +sub MAIN +( + Int:D $N, #= Numerator (an integer) + Int:D $D where { $D != 0 } #= Denominator (a non-zero integer) +) +#============================================================================== +{ + "Input: \$N = $N, \$D = $D".put; + + my Rat $fraction = Rat.new( $N, $D ); + my Str ($non-rep, $rep) = $fraction.base-repeating( $BASE ); + + qq[Output: "%s%s"\n].printf: $non-rep, $rep ?? "($rep)" !! ''; +} + +#------------------------------------------------------------------------------ +sub USAGE() +#------------------------------------------------------------------------------ +{ + my Str $usage = $*USAGE; + + $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/; + $usage.put; +} + +############################################################################## |
