diff options
| -rw-r--r-- | challenge-161/alexander-pankoff/perl/DictReader.pm | 30 | ||||
| -rw-r--r-- | challenge-161/alexander-pankoff/perl/My/List/Util.pm | 24 | ||||
| -rwxr-xr-x | challenge-161/alexander-pankoff/perl/ch-1.pl | 48 | ||||
| -rwxr-xr-x | challenge-161/alexander-pankoff/perl/ch-2.pl | 128 |
4 files changed, 230 insertions, 0 deletions
diff --git a/challenge-161/alexander-pankoff/perl/DictReader.pm b/challenge-161/alexander-pankoff/perl/DictReader.pm new file mode 100644 index 0000000000..e1dd09f749 --- /dev/null +++ b/challenge-161/alexander-pankoff/perl/DictReader.pm @@ -0,0 +1,30 @@ + +package DictReader; +use strict; +use warnings; +use autodie; +use feature qw'say state signatures'; +no warnings qw'experimental::signatures'; + +use FindBin (); +use File::Spec (); + +use Exporter qw(import); + +our @EXPORT_OK = qw(read_dict); + +use constant DICTIONARY => + File::Spec->catfile( $FindBin::RealBin, qw/.. .. .. data dictionary.txt/ ); + +sub read_dict() { + my @dict = slurp(DICTIONARY); +} + +sub slurp($file) { + open( my $fh, '<', $file ); + chomp( my @in = <$fh> ); + close $fh; + return @in; +} + +1; diff --git a/challenge-161/alexander-pankoff/perl/My/List/Util.pm b/challenge-161/alexander-pankoff/perl/My/List/Util.pm new file mode 100644 index 0000000000..c9957786da --- /dev/null +++ b/challenge-161/alexander-pankoff/perl/My/List/Util.pm @@ -0,0 +1,24 @@ +package My::List::Util; + +use strict; +use warnings; +use feature qw'signatures'; +no warnings qw'experimental::signatures'; + +use List::Util qw(reduce); +use Exporter qw(import); + +our @EXPORT_OK = qw(group_by); + +sub group_by ( $fn, @xs ) { + reduce( + sub { + push @{ $a->{ $fn->($b) } }, $b; + return $a; + }, + {}, + @xs + ); +} + +1; diff --git a/challenge-161/alexander-pankoff/perl/ch-1.pl b/challenge-161/alexander-pankoff/perl/ch-1.pl new file mode 100755 index 0000000000..d7e1873dd8 --- /dev/null +++ b/challenge-161/alexander-pankoff/perl/ch-1.pl @@ -0,0 +1,48 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use autodie; +use feature qw'say state signatures'; +no warnings qw'experimental::signatures'; + +# Task 1: Abecedarian Words +# Submitted by: Ryan J Thompson +# +# An abecedarian word is a word whose letters are arranged in alphabetical +# order. For example, “knotty” is an abecedarian word, but “knots” is not. +# Output or return a list of all abecedarian words in the dictionary, sorted in +# decreasing order of length. +# +# Optionally, using only abecedarian words, leave a short comment in your code +# to make your reviewer smile. + +use FindBin (); + +use lib $FindBin::RealBin; + +use DictReader qw(read_dict); +use My::List::Util qw(group_by); + +run() unless caller(); + +sub run() { + my @dict = read_dict(); + + # filter abecedarian words and group them by length. + my $abecedarian_by_length = group_by( sub($a) { return length $a }, + grep( is_abecedarian($_), @dict ) ); + + # output them sorted by length (shortest first). Since the input dict is + # sorted lexically, all words of them same length will be output in lexical + # order as well. + say + for map { @{ $abecedarian_by_length->{$_} } } + sort { $a <=> $b } keys %{$abecedarian_by_length}; +} + +sub is_abecedarian($word) { + + # To find out if a word is abecedarian we sort it lexically and compare to + # the input words. + return join( '', sort ( split( //, $word ) ) ) eq $word; +} diff --git a/challenge-161/alexander-pankoff/perl/ch-2.pl b/challenge-161/alexander-pankoff/perl/ch-2.pl new file mode 100755 index 0000000000..374f797e0a --- /dev/null +++ b/challenge-161/alexander-pankoff/perl/ch-2.pl @@ -0,0 +1,128 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use feature qw'say state signatures'; +no warnings qw'experimental::signatures'; + +# Task 2: Pangrams +# Submitted by: Ryan J Thompson +# +# A pangram is a sentence or phrase that uses every letter in the English +# alphabet at least once. For example, perhaps the most well known pangram is: +# +# the quick brown fox jumps over the lazy dog +# +# Using the provided dictionary, so that you don’t need to include individual +# copy, generate at least one pangram. +# +# Your pangram does not have to be a syntactically valid English sentence (doing +# so would require far more work, and a dictionary of nouns, verbs, adjectives, +# adverbs, and conjunctions). Also note that repeated letters, and even repeated +# words, are permitted. + +# BONUS: Constrain or optimize for something interesting (completely up to you), +# such as: +# +# * Shortest possible pangram (difficult) +# * Pangram which contains only abecedarian words (see challenge 1) +# * Pangram such that each word "solves" exactly one new letter. For example, +# such a pangram might begin with (newly solved letters in bold): +# a ah hi hid die ice tea ... +# * What is the longest possible pangram generated with this method? (All +# solutions will contain 26 words, so focus on the letter count.) +# * Pangrams that have the weirdest (PG-13) Google image search results +# Anything interesting goes! + +use FindBin (); +use File::Spec (); +use List::Util qw(any max); + +use lib $FindBin::RealBin; + +use DictReader qw(read_dict); +use My::List::Util qw(group_by); + +package challenge1 { + ## we want to reuse `is_abecedarian` from challenge 1 + + BEGIN { + require File::Spec->catfile( $FindBin::Bin, 'ch-1.pl' ); + } +} + +run() unless caller(); + +sub run() { + my @dict = read_dict(); + + say find_pangram_naive(@dict); + say find_pangram_most_new_chars_used(@dict); + + my @abecedarian = grep ( challenge1::is_abecedarian($_), @dict ); + + say find_pangram_naive(@abecedarian); + say find_pangram_most_new_chars_used(@abecedarian); +} + +sub find_pangram_naive(@dict) { + ## Walks the dict in order. As soon as we encounter a word with unused + ## chars it is added to the pangram. + + my @unused = 'a' .. 'z'; + my @pangram; + for my $word (@dict) { + my @new_chars = grep { contains( $word, $_ ) } @unused; + push @pangram, $word if @new_chars; + @unused = without( \@unused, \@new_chars ); + if ( !@unused ) { + return join( " ", @pangram ); + } + } + + die "No pangram found"; +} + +sub find_pangram_most_new_chars_used(@dict) { + ## In every step we find words with the most unused characters and add the + ## shorted to the pangram. + + my @unused = 'a' .. 'z'; + + my @pangram; + while (@unused) { + my $cur = find_word_with_most_unused_chars( \@dict, \@unused ); + @unused = without( \@unused, [ explode($cur) ] ); + push @pangram, $cur; + } + + return join( " ", @pangram ); +} + +sub find_word_with_most_unused_chars ( $dict, $unused ) { + my $words_by_unused = group_by( + sub ($word) { + scalar grep { contains( $word, $_ ) } @$unused; + }, + @$dict + ); + + my $shortest = ( sort { length $a <=> length $b } + @{ $words_by_unused->{ max( keys %$words_by_unused ) } } )[0]; + + return $shortest; +} + +sub contains ( $word, $char ) { + return $word =~ m/\Q$char\E/; +} + +sub without ( $as, $bs ) { + grep { + my $a = $_; + !any { $_ eq $a } @$bs + } @$as; +} + +sub explode($str) { + split( m//, $str ); +} |
