aboutsummaryrefslogtreecommitdiff
path: root/challenge-071
diff options
context:
space:
mode:
authorPerlMonk Athanasius <PerlMonk.Athanasius@gmail.com>2020-08-01 00:29:52 -0700
committerPerlMonk Athanasius <PerlMonk.Athanasius@gmail.com>2020-08-01 00:29:52 -0700
commitcf1a1e2eeb458c8b2acc49bf2b49b9f9eac1cda6 (patch)
tree9e8fa0425c4ad11a3503e09a5e168fb225b55d23 /challenge-071
parent3b0c79ec93149d7dfd7adf3e1cc08f7de1a42287 (diff)
downloadperlweeklychallenge-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.pl111
-rw-r--r--challenge-071/athanasius/perl/ch-2.pl268
-rw-r--r--challenge-071/athanasius/raku/ch-1.raku103
-rw-r--r--challenge-071/athanasius/raku/ch-2.raku210
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;
+}
+
+################################################################################