aboutsummaryrefslogtreecommitdiff
path: root/challenge-026
diff options
context:
space:
mode:
authorPerlMonk Athanasius <PerlMonk.Athanasius@gmail.com>2019-09-21 00:13:34 -0700
committerPerlMonk Athanasius <PerlMonk.Athanasius@gmail.com>2019-09-21 00:13:34 -0700
commitc2037dd70b9e3089b1ea0113d389ab1d27de019c (patch)
treecd642a764639c4c8b8e3949d3f8a4c6a64c922c3 /challenge-026
parent46742d04f0c11a82dd4db224590380788c05b15e (diff)
downloadperlweeklychallenge-club-c2037dd70b9e3089b1ea0113d389ab1d27de019c.tar.gz
perlweeklychallenge-club-c2037dd70b9e3089b1ea0113d389ab1d27de019c.tar.bz2
perlweeklychallenge-club-c2037dd70b9e3089b1ea0113d389ab1d27de019c.zip
Perl 5 & 6 solutions to Tasks 1 & 2 of the Perl Weekly Challenge #026
On branch branch-for-challenge-026 Changes to be committed: new file: perl5/ch-1.pl new file: perl5/ch-2.pl new file: perl6/ch-1.p6 new file: perl6/ch-2.p6
Diffstat (limited to 'challenge-026')
-rw-r--r--challenge-026/athanasius/perl5/ch-1.pl86
-rw-r--r--challenge-026/athanasius/perl5/ch-2.pl126
-rw-r--r--challenge-026/athanasius/perl6/ch-1.p668
-rw-r--r--challenge-026/athanasius/perl6/ch-2.p6121
4 files changed, 401 insertions, 0 deletions
diff --git a/challenge-026/athanasius/perl5/ch-1.pl b/challenge-026/athanasius/perl5/ch-1.pl
new file mode 100644
index 0000000000..e3682b69ca
--- /dev/null
+++ b/challenge-026/athanasius/perl5/ch-1.pl
@@ -0,0 +1,86 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 026
+=========================
+
+Task #1
+-------
+Create a script that accepts two strings, let us call it, *"stones"* and
+*"jewels"*. It should print the count of "alphabet" from the string *"stones"*
+found in the string *"jewels"*. For example, if your *stones* is *"chancellor"*
+and *"jewels"* is *"chocolate"*, then the script should print *"8"*. To keep it
+simple, only A-Z,a-z characters are acceptable. Also make the comparison case
+sensitive.
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2019 PerlMonk Athanasius #
+#--------------------------------------#
+
+use strict;
+use warnings;
+use Const::Fast;
+use Getopt::Long;
+
+const my $ALPHA => qr{ [A-Za-z] }x;
+const my $STONES => 'chancellor';
+const my $JEWELS => 'chocolate';
+const my $USAGE => "USAGE: perl $0 [--stones=<Str>] [--jewels=<Str>] " .
+ "[--show]\n";
+
+BEGIN
+{
+ $| = 1;
+ print "\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ my $stones = $STONES;
+ my $jewels = $JEWELS;
+ my $show = 0;
+
+ GetOptions
+ (
+ 'stones=s' => \$stones,
+ 'jewels=s' => \$jewels,
+ 'show' => \$show,
+
+ ) or die $USAGE;
+
+ my $count = 0;
+ my %jewels = map { $_ => undef } grep { /$ALPHA/ } split //, $jewels;
+
+ # Count each letter in the "stones" string if and only if it also occurs
+ # (anywhere, but at least once) in the "jewels" string
+
+ my @letters if $show;
+
+ for my $letter (grep { /$ALPHA/ } split //, $stones)
+ {
+ if (exists $jewels{$letter})
+ {
+ ++$count ;
+ push(@letters, $letter) if $show;
+ }
+ }
+
+ my $width = length $count;
+
+ printf "%*d of the letters in the Stones string \"%s\"\n" .
+ "%*s also occur%s in the Jewels string \"%s\"\n",
+ $width, $count, $stones,
+ ($count == 1 ? $width - 1 : $width), '',
+ ($count == 1 ? 's' : '' ), $jewels;
+
+ print('namely (', join(', ', @letters), ")\n") if $show;
+}
+
+################################################################################
diff --git a/challenge-026/athanasius/perl5/ch-2.pl b/challenge-026/athanasius/perl5/ch-2.pl
new file mode 100644
index 0000000000..4083d0ce86
--- /dev/null
+++ b/challenge-026/athanasius/perl5/ch-2.pl
@@ -0,0 +1,126 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 026
+=========================
+
+Task #2
+-------
+Create a script that prints *mean angles* of the given list of angles in
+degrees. Please read [ https://en.wikipedia.org/wiki/Mean_of_circular_quantities
+|wiki page] that explains the formula in details with an example.
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2019 PerlMonk Athanasius #
+#--------------------------------------#
+
+use strict;
+use warnings;
+use utf8;
+use Const::Fast;
+use Data::Types qw( is_float );
+use Getopt::Long;
+
+const my $PI => 4 * atan2(1, 1);
+const my $USAGE =>
+ "USAGE:\n perl $0 [<angles> ...]\n" .
+ " perl $0 -- [<angles> ...] (to include +/- prefixes)\n" .
+ " perl $0 [--filename=<Str>]\n";
+
+BEGIN
+{
+ $| = 1;
+ print "\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ # Parse the command line
+
+ GetOptions('file=s' => \my $file)
+ or die $USAGE;
+
+ defined $file && scalar @ARGV > 0
+ and die "ERROR: Found filename as well as angle(s)\n$USAGE";
+
+ # Read and validate the input data
+
+ my @angles = defined $file ? read_file($file) : @ARGV;
+
+ scalar @angles > 0
+ or die $USAGE;
+
+ for my $angle (@angles)
+ {
+ is_float($angle)
+ or die "ERROR: Invalid angle \"$angle\"\n$USAGE";
+ }
+
+ # Calculate and display the circular mean
+
+ printf "The circular mean of the angle%s\n (%s)\nis %s°\n",
+ scalar @angles == 1 ? '' : 's',
+ join(', ', map { "$_°" } @angles),
+ sprintf find_circular_mean(@angles);
+}
+
+#-------------------------------------------------------------------------------
+sub read_file
+#-------------------------------------------------------------------------------
+{
+ my ($file) = @_;
+ my @angles;
+
+ open my $fh, '<', $file
+ or die "Cannot open file \"$file\" for reading, stopped";
+
+ # File format: one angle (in degrees) per line; blank lines are ignored
+
+ while (my $line = <$fh>)
+ {
+ $line =~ s/ ^ \s+ //x; # trim leading whitespace
+ $line =~ s/ \s+ $ //x; # trim trailing whitespace (including newline)
+
+ next if $line eq '';
+
+ push @angles, $line;
+ }
+
+ close $fh
+ or die "Cannot close file \"$file\", stopped";
+
+ return @angles;
+}
+
+#-------------------------------------------------------------------------------
+sub find_circular_mean
+#-------------------------------------------------------------------------------
+{
+ # The circular mean (in radians) is given by the formula:
+ #
+ # atan2( 1/n ∑ [j=1..n] sin α_j, 1/n ∑ [j=1..n] cos α_j )
+
+ my @angles = @_;
+ my $sum_of_sines = 0;
+ my $sum_of_cosines = 0;
+
+ for my $degrees (@angles)
+ {
+ my $radians = $degrees * ($PI / 180);
+ $sum_of_sines += sin $radians; # build ∑ [j=1..n] sin α_j
+ $sum_of_cosines += cos $radians; # build ∑ [j=1..n] cos α_j
+ }
+
+ my $n = scalar @angles;
+
+ return atan2($sum_of_sines / $n, $sum_of_cosines / $n) * (180 / $PI);
+}
+
+################################################################################
diff --git a/challenge-026/athanasius/perl6/ch-1.p6 b/challenge-026/athanasius/perl6/ch-1.p6
new file mode 100644
index 0000000000..29f9663afb
--- /dev/null
+++ b/challenge-026/athanasius/perl6/ch-1.p6
@@ -0,0 +1,68 @@
+use v6;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 026
+=========================
+
+Task #1
+-------
+Create a script that accepts two strings, let us call it, *"stones"* and
+*"jewels"*. It should print the count of "alphabet" from the string *"stones"*
+found in the string *"jewels"*. For example, if your *stones* is *"chancellor"*
+and *"jewels"* is *"chocolate"*, then the script should print *"8"*. To keep it
+simple, only A-Z,a-z characters are acceptable. Also make the comparison case
+sensitive.
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2019 PerlMonk Athanasius #
+#--------------------------------------#
+
+my Regex constant $ALPHA = rx{ <[A..Za..z]> };
+my Str constant $STONES = 'chancellor';
+my Str constant $JEWELS = 'chocolate';
+
+BEGIN say '';
+
+#===============================================================================
+sub MAIN
+#===============================================================================
+(
+ Str:D :$stones = $STONES, #= The "stones" string
+ Str:D :$jewels = $JEWELS, #= The "jewels" string
+ Bool:D :$show = False, #= Show the matching letters?
+)
+{
+ my UInt $count = 0;
+ my Nil %jewels = $jewels.split('').grep( { $ALPHA } ).map( { $_ => Nil } );
+
+ # Count each letter in the "stones" string if and only if it also occurs
+ # (anywhere, but at least once) in the "jewels" string
+
+ my Str @letters if $show;
+
+ for $stones.split('').grep( { $ALPHA } ) -> Str $letter
+ {
+ if %jewels{$letter}:exists
+ {
+ ++$count;
+ @letters.push($letter) if $show;
+ }
+ }
+
+ my UInt $width = $count.chars;
+
+ ("%*d of the letters in the Stones string \"%s\"\n" ~
+ "%*s also occur%s in the Jewels string \"%s\"\n").printf:
+ $width, $count, $stones,
+ ($count == 1 ?? $width - 1 !! $width), '',
+ ($count == 1 ?? 's' !! '' ), $jewels;
+
+ "namely ({ @letters.join(', ') })".say if $show && @letters.elems;
+}
+
+################################################################################
diff --git a/challenge-026/athanasius/perl6/ch-2.p6 b/challenge-026/athanasius/perl6/ch-2.p6
new file mode 100644
index 0000000000..612b67c15a
--- /dev/null
+++ b/challenge-026/athanasius/perl6/ch-2.p6
@@ -0,0 +1,121 @@
+use v6;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 026
+=========================
+
+Task #2
+-------
+Create a script that prints *mean angles* of the given list of angles in
+degrees. Please read [ https://en.wikipedia.org/wiki/Mean_of_circular_quantities
+|wiki page] that explains the formula in details with an example.
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2019 PerlMonk Athanasius #
+#--------------------------------------#
+
+BEGIN say '';
+
+#===============================================================================
+multi sub MAIN(Str:D :$file) #= Data file containing angles (degrees),
+ #= one per line
+#===============================================================================
+{
+ my Real @angles;
+
+ for $file.IO.lines
+ {
+ my $line = $_;
+
+ $line ~~ s/ ^^ \s+ //;
+ $line ~~ s/ \s+ $$ //;
+
+ next unless $line.chars > 0;
+
+ use fatal;
+
+ @angles.push: val($line, :val-or-fail);
+ }
+
+ MAIN(@angles);
+
+ CATCH
+ {
+ when X::Str::Numeric
+ {
+ $*ERR.say: "In file \"$file\":\n{ .message }\n$*USAGE";
+ }
+ }
+}
+
+#-------------------------------------------------------------------------------
+class X::Mean-Angles::No-Angles is Exception
+#-------------------------------------------------------------------------------
+{
+ method message(--> Str:D)
+ {
+ return 'No angles found';
+ }
+}
+
+#===============================================================================
+multi sub MAIN(*@angles) #= One or more angles (all in degrees)
+#===============================================================================
+{
+ my Real:D @real-angles = @angles;
+
+ @real-angles.elems > 0
+ or X::Mean-Angles::No-Angles.new.throw;
+
+ # Calculate and display the circular mean
+
+ $*OUT.encoding('windows-1252');
+
+ "The circular mean of the angle%s\n (%s)\nis %.1f°\n".printf:
+ @angles.elems == 1 ?? '' !! 's',
+ @angles.map( { "$_°" } ).join(', '),
+ find-circular-mean(@angles);
+
+ CATCH
+ {
+ when X::TypeCheck::Assignment
+ {
+ $*ERR.say: .message ~ "\n$*USAGE";
+ }
+
+ when X::Mean-Angles::No-Angles
+ {
+ $*ERR.say: .message ~ "\n$*USAGE";
+ }
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find-circular-mean(*@angles --> Real:D)
+#-------------------------------------------------------------------------------
+{
+ # The circular mean (in radians) is given by the formula:
+ #
+ # atan2( 1/n ∑ [j=1..n] sin α_j, 1/n ∑ [j=1..n] cos α_j )
+
+ my Real $sum-of-sines = 0;
+ my Real $sum-of-cosines = 0;
+
+ for @angles -> Real $degrees
+ {
+ my Real $radians = $degrees * (π / 180);
+ $sum-of-sines += $radians.sin; # build ∑ [j=1..n] sin α_j
+ $sum-of-cosines += $radians.cos; # build ∑ [j=1..n] cos α_j
+ }
+
+ my UInt $n = @angles.elems;
+
+ return ($sum-of-sines / $n).atan2($sum-of-cosines / $n) * (180 / π);
+}
+
+################################################################################