diff options
| author | PerlMonk Athanasius <PerlMonk.Athanasius@gmail.com> | 2019-09-21 00:13:34 -0700 |
|---|---|---|
| committer | PerlMonk Athanasius <PerlMonk.Athanasius@gmail.com> | 2019-09-21 00:13:34 -0700 |
| commit | c2037dd70b9e3089b1ea0113d389ab1d27de019c (patch) | |
| tree | cd642a764639c4c8b8e3949d3f8a4c6a64c922c3 /challenge-026 | |
| parent | 46742d04f0c11a82dd4db224590380788c05b15e (diff) | |
| download | perlweeklychallenge-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.pl | 86 | ||||
| -rw-r--r-- | challenge-026/athanasius/perl5/ch-2.pl | 126 | ||||
| -rw-r--r-- | challenge-026/athanasius/perl6/ch-1.p6 | 68 | ||||
| -rw-r--r-- | challenge-026/athanasius/perl6/ch-2.p6 | 121 |
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 / π); +} + +################################################################################ |
