diff options
| author | PerlMonk Athanasius <PerlMonk.Athanasius@gmail.com> | 2020-08-01 00:29:52 -0700 |
|---|---|---|
| committer | PerlMonk Athanasius <PerlMonk.Athanasius@gmail.com> | 2020-08-01 00:29:52 -0700 |
| commit | cf1a1e2eeb458c8b2acc49bf2b49b9f9eac1cda6 (patch) | |
| tree | 9e8fa0425c4ad11a3503e09a5e168fb225b55d23 /challenge-071 | |
| parent | 3b0c79ec93149d7dfd7adf3e1cc08f7de1a42287 (diff) | |
| download | perlweeklychallenge-club-cf1a1e2eeb458c8b2acc49bf2b49b9f9eac1cda6.tar.gz perlweeklychallenge-club-cf1a1e2eeb458c8b2acc49bf2b49b9f9eac1cda6.tar.bz2 perlweeklychallenge-club-cf1a1e2eeb458c8b2acc49bf2b49b9f9eac1cda6.zip | |
Perl & Raku solutions to Tasks 1 & 2 of the Perl Weekly Challenge #071
On branch branch-for-challenge-071
Changes to be committed:
new file: challenge-071/athanasius/perl/ch-1.pl
new file: challenge-071/athanasius/perl/ch-2.pl
new file: challenge-071/athanasius/raku/ch-1.raku
new file: challenge-071/athanasius/raku/ch-2.raku
Diffstat (limited to 'challenge-071')
| -rw-r--r-- | challenge-071/athanasius/perl/ch-1.pl | 111 | ||||
| -rw-r--r-- | challenge-071/athanasius/perl/ch-2.pl | 268 | ||||
| -rw-r--r-- | challenge-071/athanasius/raku/ch-1.raku | 103 | ||||
| -rw-r--r-- | challenge-071/athanasius/raku/ch-2.raku | 210 |
4 files changed, 692 insertions, 0 deletions
diff --git a/challenge-071/athanasius/perl/ch-1.pl b/challenge-071/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..0fa2e939da --- /dev/null +++ b/challenge-071/athanasius/perl/ch-1.pl @@ -0,0 +1,111 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 071 +========================= + +Task #1 +------- +*Peak Element* + +*Submitted by:* Mohammad S Anwar + +You are given positive integer _$N_ (>1). + +Write a script to create an array of size _$N_ with random unique elements +between _1_ and _50_. + +In the end it should print _peak elements_ in the array, if found. + + An array element is called peak if it is bigger than its neighbour[s]. + +*Example 1* + +Array: [ 18, 45, 38, 25, 10, 7, 21, 6, 28, 48 ] +Peak: [ 48, 45, 21 ] + +*Example 2* + +Array: [ 47, 11, 32, 8, 1, 9, 39, 14, 36, 23 ] +Peak: [ 47, 32, 39, 36 ] + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2020 PerlMonk Athanasius # +#--------------------------------------# + +use strict; +use warnings; +use Const::Fast; +use List::Util qw( sample ); +use Regexp::Common qw( number ); + +const my $MAX => 50; +const my $USAGE => +"Usage: + raku ch-1.raku <N> + + <N> Array size: N is an integer such that 1 < N <= $MAX\n"; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 071, Task #1: Peak Element (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + my $N = parse_command_line(); + + # List::Util::sample(): "Randomly select the given number of elements from + # the input list. Any given position in the input list will be selected at + # most once." + + my @array = sample $N, 1 .. $MAX; + my $peaks = find_peaks(\@array); + + print 'Array: [ ', join(', ', @array ), " ]\n", + 'Peak: [ ', join(', ', @$peaks), " ]\n"; +} + +#------------------------------------------------------------------------------- +sub find_peaks +#------------------------------------------------------------------------------- +{ + my ($array) = @_; + my @peaks; + push @peaks, $array->[0] if $array->[0] > $array->[1]; # First element + + for my $i (1 .. $#$array - 1) # Intermediate elements + { + push @peaks, $array->[$i] if $array->[$i] > $array->[$i - 1] && + $array->[$i] > $array->[$i + 1]; + } + + push @peaks, $array->[-1] if $array->[-1] > $array->[-2]; # Final element + + return \@peaks; +} + +#------------------------------------------------------------------------------- +sub parse_command_line +#------------------------------------------------------------------------------- +{ + scalar @ARGV == 1 or die $USAGE; + + my $N = $ARGV[0]; + $N =~ /\A$RE{num}{int}\z/ or die $USAGE; + $N > 1 && $N <= $MAX or die $USAGE; + + return $N; +} + +################################################################################ diff --git a/challenge-071/athanasius/perl/ch-2.pl b/challenge-071/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..8b3b4533d9 --- /dev/null +++ b/challenge-071/athanasius/perl/ch-2.pl @@ -0,0 +1,268 @@ +#!perl + +################################################################################ +=comment + +Perl Weekly Challenge 071 +========================= + +Task #2 +------- +*Trim Linked List* + +*Submitted by:* Mohammad S Anwar + +You are given a singly linked list and a positive integer _$N_ (>0). + +Write a script to remove the _$Nth_ node from the end of the linked list and +print the linked list. + +If _$N_ is greater than the size of the linked list then remove the first node +of the list. + +NOTE: Please use pure linked list implementation. + +*Example* + +Given Linked List: 1 -> 2 -> 3 -> 4 -> 5 + +when $N = 1 +Output: 1 -> 2 -> 3 -> 4 + +when $N = 2 +Output: 1 -> 2 -> 3 -> 5 + +when $N = 3 +Output: 1 -> 2 -> 4 -> 5 + +when $N = 4 +Output: 1 -> 3 -> 4 -> 5 + +when $N = 5 +Output: 2 -> 3 -> 4 -> 5 + +when $N = 6 +Output: 2 -> 3 -> 4 -> 5 + +=cut +################################################################################ + +#--------------------------------------# +# Copyright © 2020 PerlMonk Athanasius # +#--------------------------------------# + +#******************************************************************************* +=comment + +For a doubly-linked list, counting backwards from the tail of the list would be +straightforward. For a singly-linked list, it is necessary to first know the +total number of elements in the list (i.e., its size); once this is known, it is +easy to find the required node by counting forwards from the head. + +One way to find the list size is to traverse the whole list from head to tail, +counting the traversals. For long lists, this could be a costly procedure. The +following SinglyLinkedList implementation therefore adds an "elems" attribute +which records the list size. This value is maintained by ensuring that it is + - incremented on each call to append() or insert() + - decremented on each call to remove(). + +Note: The implementation provided for SinglyLinkedList is only the minimum +needed for this Task. Method insert() is omitted. A robust implementation would +also provide a dedicated iterator for list traversal. + +=cut +#******************************************************************************* + +#=============================================================================== +package Node +#=============================================================================== +{ + use Moose; + use MooseX::LvalueAttribute; + use namespace::autoclean; + + has datum => + ( + is => 'ro', + isa => 'Str', + required => 1, + ); + + has next => + ( + traits => ['Lvalue'], + is => 'rw', + isa => 'Maybe[Node]', + default => undef, + ); + + no Moose; + __PACKAGE__->meta->make_immutable; +} + +#=============================================================================== +package SinglyLinkedList +#=============================================================================== +{ + use Moose; + use Moose::Util::TypeConstraints; + use MooseX::LvalueAttribute; + use namespace::autoclean; + + subtype 'UInt', + as 'Int', + where { $_ >= 0 }; + + has elems => # List size + ( + traits => ['Lvalue'], + is => 'rw', + isa => 'UInt', + default => 0, + ); + + has head => # Sentinel + ( + is => 'ro', + isa => 'Node', + default => sub { Node->new(datum => 'HEAD') }, + ); + + #--------------------------------------------------------------------------- + sub append + #--------------------------------------------------------------------------- + { + my ($self, $element) = @_; + my $node = Node->new(datum => $element); + my $current = $self->head; + $current = $current->next while $current->next; + $current->next = $node; + + ++$self->elems; + + return $self; # Facilitate chaining + } + + #--------------------------------------------------------------------------- + sub remove + #--------------------------------------------------------------------------- + { + my ($self, $preceding) = @_; + my $target; + + $preceding or die "ERROR: Invalid 'preceding' node passed to remove()"; + + if ($preceding->next) + { + $target = $preceding->next; + $preceding->next = $target->next; + + --$self->elems; + } + else # Sanity check only + { + $self->elems == 0 + or die 'ERROR: elems = ' . $self->elems . ', should be 0'; + } + + return $self; # Facilitate chaining + } + + #--------------------------------------------------------------------------- + sub print + #--------------------------------------------------------------------------- + { + my ($self, $title) = @_; + + printf '%s [%d]: ', $title, $self->elems; + + if ($self->elems > 0) + { + my $current = $self->head->next; + + print $current->datum; + printf ' -> %s', $current->datum while $current = $current->next; + print "\n"; + } + else + { + print "<empty>\n"; + } + } + + no Moose; + __PACKAGE__->meta->make_immutable; +} + +################################################################################ + +use strict; +use warnings; +use Const::Fast; +use Getopt::Long; +use Regexp::Common qw( number ); + +const my $USAGE => +"Usage: + perl $0 [-N <Natural>] [-S <UInt>] + perl $0 [-N <Natural>] [<elements> ...] + + -N <Natural> No. of the node to remove, counting from the list end + -S <UInt> List size: elements will have values 1, 2, 3, ..., S + [<elements> ...] Explicit element values (strings), in order\n"; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + $| = 1; + print "\nChallenge 071, Task #2: Trim Linked List (Perl)\n\n"; +} + +#=============================================================================== +MAIN: +#=============================================================================== +{ + my ($N, $elements) = parse_command_line(); + + # 1. Build and display the linked list + + my $list = SinglyLinkedList->new; + $list->append($_) for @$elements; + $list->print('Input '); + + # 2. Remove the Nth-last element and display the resulting list + + my $diff = $list->elems - $N; + my $count = $diff < 0 ? 0 : $diff; + my $prev = $list->head; + $prev = $prev->next for 1 .. $count; + + $list->remove($prev) + ->print("N = $N\nOutput"); +} + +#------------------------------------------------------------------------------- +sub parse_command_line +#------------------------------------------------------------------------------- +{ + my ($N, $S); + + GetOptions('N=i' => \$N, 'S:i' => \$S) or die $USAGE; + + my @elements = @ARGV; + + defined $N && $N =~ /\A$RE{num}{int}\z/ && $N > 0 or die $USAGE; + + if (defined $S) + { + scalar @elements == 0 && + $S =~ /\A$RE{num}{int}\z/ && $S >= 0 or die $USAGE; + + @elements = 1 .. $S; + } + + return ($N, \@elements); +} + +################################################################################ diff --git a/challenge-071/athanasius/raku/ch-1.raku b/challenge-071/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..7317503b69 --- /dev/null +++ b/challenge-071/athanasius/raku/ch-1.raku @@ -0,0 +1,103 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 071 +========================= + +Task #1 +------- +*Peak Element* + +*Submitted by:* Mohammad S Anwar + +You are given positive integer _$N_ (>1). + +Write a script to create an array of size _$N_ with random unique elements +between _1_ and _50_. + +In the end it should print _peak elements_ in the array, if found. + + An array element is called peak if it is bigger than its neighbour[s]. + +*Example 1* + +Array: [ 18, 45, 38, 25, 10, 7, 21, 6, 28, 48 ] +Peak: [ 48, 45, 21 ] + +*Example 2* + +Array: [ 47, 11, 32, 8, 1, 9, 39, 14, 36, 23 ] +Peak: [ 47, 32, 39, 36 ] + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2020 PerlMonk Athanasius # +#--------------------------------------# + +my UInt constant $MAX = 50; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 071, Task #1: Peak Element (Raku)\n".put; +} + +#=============================================================================== +sub MAIN +( + #| Array size: N is an integer such that 1 < N <= MAX + + UInt:D $N where { $N > 1 && $N <= $MAX } +) +#=============================================================================== +{ + # Method pick() of class List: "Returns $count elements chosen at random and + # without repetition from the invocant." + + my UInt @array = (1 .. $MAX).pick: $N; + my UInt @peaks = find-peaks(@array); + + "Array: [ %s ]\n".printf: @array.join: ', '; + "Peak: [ %s ]\n".printf: @peaks.join: ', '; +} + +#------------------------------------------------------------------------------- +sub find-peaks +( + Array:D[UInt:D] $array +--> Array:D[UInt:D] +) +#------------------------------------------------------------------------------- +{ + my UInt @peaks; + + @peaks.push: $array[0] if $array[0] > $array[1]; # First element + + for 1 .. $array.elems - 2 -> UInt $i # Intermediate elements + { + @peaks.push: $array[$i] if $array[$i] > $array[$i - 1] && + $array[$i] > $array[$i + 1]; + } + + @peaks.push: $array[*-1] if $array[*-1] > $array[*-2]; # Final element + + return @peaks; +} + +#------------------------------------------------------------------------------- +sub USAGE() +#------------------------------------------------------------------------------- +{ + my Str $usage = $*USAGE; + + $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/; + $usage ~~ s/ MAX /$MAX/; + $usage.put; +} + +################################################################################ diff --git a/challenge-071/athanasius/raku/ch-2.raku b/challenge-071/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..a0b0610ca4 --- /dev/null +++ b/challenge-071/athanasius/raku/ch-2.raku @@ -0,0 +1,210 @@ +use v6d; + +################################################################################ +=begin comment + +Perl Weekly Challenge 071 +========================= + +Task #2 +------- +*Trim Linked List* + +*Submitted by:* Mohammad S Anwar + +You are given a singly linked list and a positive integer _$N_ (>0). + +Write a script to remove the _$Nth_ node from the end of the linked list and +print the linked list. + +If _$N_ is greater than the size of the linked list then remove the first node +of the list. + +NOTE: Please use pure linked list implementation. + +*Example* + +Given Linked List: 1 -> 2 -> 3 -> 4 -> 5 + +when $N = 1 +Output: 1 -> 2 -> 3 -> 4 + +when $N = 2 +Output: 1 -> 2 -> 3 -> 5 + +when $N = 3 +Output: 1 -> 2 -> 4 -> 5 + +when $N = 4 +Output: 1 -> 3 -> 4 -> 5 + +when $N = 5 +Output: 2 -> 3 -> 4 -> 5 + +when $N = 6 +Output: 2 -> 3 -> 4 -> 5 + +=end comment +################################################################################ + +#--------------------------------------# +# Copyright © 2020 PerlMonk Athanasius # +#--------------------------------------# + +#******************************************************************************* +=begin comment + +For a doubly-linked list, counting backwards from the tail of the list would be +straightforward. For a singly-linked list, it is necessary to first know the +total number of elements in the list (i.e., its size); once this is known, it is +easy to find the required node by counting forwards from the head. + +One way to find the list size is to traverse the whole list from head to tail, +counting the traversals. For long lists, this could be a costly procedure. The +following SinglyLinkedList implementation therefore adds a "$!elems" attribute +which records the list size. This value is maintained by ensuring that it is + - incremented on each call to append() or insert() + - decremented on each call to remove(). + +Note: The implementation provided for SinglyLinkedList is only the minimum +needed for this Task. Method insert() is omitted. A robust implementation would +also provide a dedicated iterator for list traversal. + +=end comment +#******************************************************************************* + +#=============================================================================== +class Node +#=============================================================================== +{ + has Str $.datum is required; + has Node $.next is rw = Nil; +} + +#=============================================================================== +class SinglyLinkedList +#=============================================================================== +{ + has UInt $.elems = 0; # List size + has Node $.head = Node.new: datum => 'HEAD'; # Sentinel + + #--------------------------------------------------------------------------- + method append(Str:D $element --> SinglyLinkedList:D) + #--------------------------------------------------------------------------- + { + my Node $node = Node.new: datum => $element; + my Node $current = $!head; + $current = $current.next while $current.next; + $current.next = $node; + + ++$!elems; + + return self; # Facilitate chaining + } + + #--------------------------------------------------------------------------- + method remove(Node:D $preceding --> SinglyLinkedList:D) + #--------------------------------------------------------------------------- + { + my Node $target; + + if $preceding.next + { + $target = $preceding.next; + $preceding.next = $target.next; + + --$!elems; + } + else # Sanity check only + { + $!elems == 0 or die "ERROR: \$!elems = $!elems, should be 0"; + } + + return self; # Facilitate chaining + } + + #--------------------------------------------------------------------------- + method print(Str:D $title) + #--------------------------------------------------------------------------- + { + "$title [$!elems]: ".print; + + if $!elems > 0 + { + (my Node $current = $!head.next).datum.print; + " -> { $current.datum }".print while $current = $current.next; + ''.put; + } + else + { + '<empty>'.put; + } + } +} + +################################################################################ + +subset Natural of UInt where * > 0; + +#------------------------------------------------------------------------------- +BEGIN +#------------------------------------------------------------------------------- +{ + "\nChallenge 071, Task #2: Trim Linked List (Raku)\n".put; +} + +#=============================================================================== +multi sub MAIN +( + Natural:D :$N, #= No. of the node to remove, counting from the list end + UInt:D :$S, #= List size: elements will have values '1', '2', ..., 'S' +) +#=============================================================================== +{ + main($N, 1 .. $S); +} + +#=============================================================================== +multi sub MAIN +( + Natural:D :$N, + *@elements, #= Explicit element values (strings), in order +) +#=============================================================================== +{ + main($N, @elements); +} + +#------------------------------------------------------------------------------- +sub main(Natural:D $N, *@elements) +#------------------------------------------------------------------------------- +{ + # 1. Build and display the linked list + + my SinglyLinkedList $list = SinglyLinkedList.new; + + $list.append: .Str for @elements; + $list.print: 'Input '; + + # 2. Remove the Nth-last element and display the resulting list + + my Int $diff = $list.elems - $N; + my UInt $count = $diff < 0 ?? 0 !! $diff; + my Node $prev = $list.head; + $prev = $prev.next for 1 .. $count; + + $list.remove($prev) + .print: "N = $N\nOutput"; +} + +#------------------------------------------------------------------------------- +sub USAGE() +#------------------------------------------------------------------------------- +{ + my Str $usage = $*USAGE; + + $usage ~~ s:g/ ($*PROGRAM-NAME) /raku $0/; + $usage.put; +} + +################################################################################ |
