aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--challenge-004/james-smith/README.md17
-rw-r--r--challenge-004/james-smith/perl5/ch-2a.pl39
-rw-r--r--challenge-004/james-smith/perl6/ch-2.p629
3 files changed, 70 insertions, 15 deletions
diff --git a/challenge-004/james-smith/README.md b/challenge-004/james-smith/README.md
index 27d23b6119..f58242a0a0 100644
--- a/challenge-004/james-smith/README.md
+++ b/challenge-004/james-smith/README.md
@@ -24,17 +24,32 @@ The first part collects together the counts of the letters and the second loops
The loop is destructive of the counts array so we pass a copy into the function rather than the usual pass by reference.. This effectively clones the counts array so we don't have to do this explicitly
+For 2a see notes about perl 6 (this is non-destructive...)
```
perl perl5/ch-2.pl back < /usr/share/dict/british-english-insane
+perl perl5/ch-2a.pl back < /usr/share/dict/british-english-insane
```
## Perl 6
-Similar code to perl 5 - just fixing syntactic sugar. Perl6 though passes by reference (which you have to do explicitly in Perl 5) so you need to clone the counts array before passing it to checkword.
+Because Perl 6 can't pass by value - I've rewritten this to be non-destructive (the checkword function counts up and compares to the main count)... need to check for performance....
The other change is split - you don't use the empty regex "//" to perform the split - rather the empty string (something frowned upon in Perl5) and to remove the rogue white-space split adds you need to include the additional flag `:skip-empty`
+I haven't golfed this one entirely - but have used "golf" techniques along the way to make the code in someways more readable using grep rather than if for instance!
```
perl6 perl6/ch-2.p6 back < /usr/share/dict/british-english-insane
```
+## Timings
+
+Yet again perl5 out performs perl 6 - perhaps I need to know how to optimize perl 6 code...
+
+```
+ perl5 ch-2.pl 1.9 seconds
+ perl5 ch-2a.pl 1.3 seconds
+ perl6 ch-2.p6 27.1 seconds
+```
+
+This time by what looks like a factor of 20.... need a Perl 6 expert to suggest why....
+
diff --git a/challenge-004/james-smith/perl5/ch-2a.pl b/challenge-004/james-smith/perl5/ch-2a.pl
new file mode 100644
index 0000000000..fd28d62886
--- /dev/null
+++ b/challenge-004/james-smith/perl5/ch-2a.pl
@@ -0,0 +1,39 @@
+use strict;
+use warnings;
+use feature qw(say);
+
+## Read in letters from command line... and store in %c...
+## We split each argument so words can be passed in rather
+## than individual letters if required...
+
+my %counts; $counts{lc$_}++ for map{split//} @ARGV;
+
+say $_ for grep { chomp $_; checkword(lc$_) } <STDIN>;
+
+## Check the word to see if it can be made up from letters
+## use passing a hash by value to clone the counts so we
+## don't destroy it through each loop {the method is
+## destructive!}
+
+sub checkword {
+ my %t;
+ foreach(split//,shift){
+ return 0 unless $counts{$_} && $t{$_}++ < $counts{$_};
+ }
+ return 1;
+}
+
+## Below is the first try - not as elegant - but no function
+## calls - just needs to use labels to jump out of inner for
+## loop
+##
+## my $c;
+## $c->{lc $_}++ foreach @ARGV;
+## WORD: while (my $w=<STDIN>) {
+## chomp $w;
+## my %t = %{$c};
+## for (split //,lc $w) {
+## next WORD if --$t{$_} < 0;
+## }
+## say $w;
+## }
diff --git a/challenge-004/james-smith/perl6/ch-2.p6 b/challenge-004/james-smith/perl6/ch-2.p6
index 1b2ad01c7c..c780d7faaa 100644
--- a/challenge-004/james-smith/perl6/ch-2.p6
+++ b/challenge-004/james-smith/perl6/ch-2.p6
@@ -1,27 +1,28 @@
use strict;
-## Read in letters from command line... and store in %c...
+## Read in letters from command line... and store in %counts...
## We split each argument so words can be passed in rather
## than individual letters if required...
+## To avoid the additional non-letter characters we have to
+## use the :skip-empty flag...
my %counts;
-for @*ARGS -> $w {
- %counts{lc $_}++ for split '',$w, :skip-empty;
+for @*ARGS {
+ %counts{lc $_}++ for split '',$_, :skip-empty;
}
-for $*IN.lines() -> $word {
- my %copy = %counts.clone;
- say $word if checkword(lc($word),%copy);
-}
+## Re-write as a one-liner by using `for grep` rather than `for {if}`...
+
+say $_ for grep { checkword(lc $_) }, $*IN.lines();
-## Check the word to see if it can be made up from letters
-## use passing a hash by value to clone the counts so we
-## don't destroy it through each loop {the method is
-## destructive!}
+## Rewritten this to make it non destructive - we count up rather than
+## down needs another check to avoid comparing to undef...
+## So this is probably a nicer way of doing it....
-sub checkword($word,%copy_counts) {
- for (split '',$word, :skip-empty) -> $letter {
- return if --%copy_counts{$letter} < 0;
+sub checkword($word) {
+ my %tmp_counts;
+ for (split '',$word, :skip-empty) {
+ return unless %counts{$_} && %tmp_counts{$_}++ < %counts{$_};
}
return 1;
}