aboutsummaryrefslogtreecommitdiff
path: root/challenge-010
diff options
context:
space:
mode:
authorFrancis Whittle <FJ.Whittle@gmail.com>2019-06-02 11:06:14 +1000
committerFrancis Whittle <FJ.Whittle@gmail.com>2019-06-02 11:06:37 +1000
commit89921e6798375fa6fb6171d337dddb1c0d1929e1 (patch)
treeb1745033394d85df0e9a7d3832dc8b7eb169b5fd /challenge-010
parent96ef0ed3f6ba2ab8d4a5d9db6af8db3e040e00cf (diff)
downloadperlweeklychallenge-club-89921e6798375fa6fb6171d337dddb1c0d1929e1.tar.gz
perlweeklychallenge-club-89921e6798375fa6fb6171d337dddb1c0d1929e1.tar.bz2
perlweeklychallenge-club-89921e6798375fa6fb6171d337dddb1c0d1929e1.zip
fjwhittle challenge 010 code documentation and blog post
Diffstat (limited to 'challenge-010')
-rw-r--r--challenge-010/fjwhittle/blog.txt1
-rw-r--r--challenge-010/fjwhittle/perl6/ch-1.p634
-rw-r--r--challenge-010/fjwhittle/perl6/ch-2.p627
3 files changed, 48 insertions, 14 deletions
diff --git a/challenge-010/fjwhittle/blog.txt b/challenge-010/fjwhittle/blog.txt
new file mode 100644
index 0000000000..072cb442a9
--- /dev/null
+++ b/challenge-010/fjwhittle/blog.txt
@@ -0,0 +1 @@
+https://rage.powered.ninja/2019/06/02/obiective-romanos-grammaticam.html
diff --git a/challenge-010/fjwhittle/perl6/ch-1.p6 b/challenge-010/fjwhittle/perl6/ch-1.p6
index 509b13840a..bbecdf4fd4 100644
--- a/challenge-010/fjwhittle/perl6/ch-1.p6
+++ b/challenge-010/fjwhittle/perl6/ch-1.p6
@@ -1,23 +1,27 @@
#!/usr/bin/env perl6
-use v6.d;
+#| Grammar to parse the generalised subtractive form Roman numerals.
+# Allows for mixed additive and subtractive notation, so IV = IIII
grammar Roman {
- regex TOP { [[ $<prefix> = 'C' ]? $<M> = 'M']* [ $<suffix> = <.D> ] }
+ regex TOP { [[ $<prefix> = 'C' ]? $<M> = 'M']* $<suffix> = <.D> }
- regex D { [[ $<prefix> = 'C' ]? $<D> = 'D']* [ $<suffix> = <.C> ]}
+ regex D { [[ $<prefix> = 'C' ]? $<D> = 'D']* $<suffix> = <.C> }
- regex C { [[ $<prefix> = 'X' ]? $<C> = 'C']* [ $<suffix> = <.L> ] }
+ regex C { [[ $<prefix> = 'X' ]? $<C> = 'C']* $<suffix> = <.L> }
- regex L { [[ $<prefix> = 'X' ]? $<L> = 'L']* [ $<suffix> = <.X> ] }
+ regex L { [[ $<prefix> = 'X' ]? $<L> = 'L']* $<suffix> = <.X> }
- regex X { [[ $<prefix> = 'I' ]? $<X> = 'X']* [ $<suffix> = <.V> ] }
+ regex X { [[ $<prefix> = 'I' ]? $<X> = 'X']* $<suffix> = <.V> }
- regex V { [[ $<prefix> = 'I' ]? $<V> = 'V']* [ $<suffix> = <.I> ] }
+ regex V { [[ $<prefix> = 'I' ]? $<V> = 'V']* $<suffix> = <.I> }
regex I { [ $<I> = 'I' ]* }
}
+#| Actions class for converting parsed Roman numerals to an integer.
+# This will happily convert multiple digits with prefixes, so you
+# could e.g. write 1889 as CMCMXLXLIVIVI instead of MDCCCLXXXIX
class RomanCalc {
method TOP($/) {
make $<M>.elems * 1000 + $<suffix>.made - 100 * $<prefix>.elems;
@@ -42,14 +46,20 @@ class RomanCalc {
}
}
+#| Converts integer input to a Roman numeral
multi convert-roman(Int $input){
my Int $num = $input;
my $roman = '';
+ # A list of pairs mapping value to numeral, ordered by magnitude.
+ # Prefixed numerals get their own entry for ease of implementation.
my @nmap = (1000 => 'M', 900 => 'CM', 500 => 'D', 400 => 'CD',
- 100 => 'C', 90 => 'XC', 50 => 'L', 40 => 'XL',
- 10 => 'X', 9 => 'IX', 5 => 'V', 4 => 'IV', 1 => 'I');
+ 100 => 'C', 90 => 'XC', 50 => 'L', 40 => 'XL',
+ 10 => 'X', 9 => 'IX', 5 => 'V', 4 => 'IV',
+ 1 => 'I');
+ # For each value => numeral mapping, output the corresponding number
+ # of digits that fits, then operate on the remainder.
for @nmap {
if $num >= .key {
$roman ~= .value x ($num div .key);
@@ -60,12 +70,12 @@ multi convert-roman(Int $input){
$roman;
}
+#| Attempts to convert String input into an integer from Roman numeral.
multi convert-roman(Str $input) {
- my $parsed = Roman.parse($input, actions => RomanCalc);
-
+ my $parsed = Roman.parse($input, actions => RomanCalc) or fail "‘$input’ is not a Roman numeral";
$parsed.made;
}
for @*ARGS -> $input {
- say $input ~ " → " ~ convert-roman(+$input || $input)
+ say try { $input ~ " → " ~ convert-roman(+$input // $input) } || "Could not convert ‘$input’"
}
diff --git a/challenge-010/fjwhittle/perl6/ch-2.p6 b/challenge-010/fjwhittle/perl6/ch-2.p6
index 152e72c29c..2a0f50925c 100644
--- a/challenge-010/fjwhittle/perl6/ch-2.p6
+++ b/challenge-010/fjwhittle/perl6/ch-2.p6
@@ -2,50 +2,73 @@
#| Jaro similarity
sub sim-j (Str $a, Str $b) {
+ # It's just easier to operate on arrays.
my @si = $a.comb;
my @sj = $b.comb;
+ # Maximum matching character distance.
my $lim = max(@si, @sj) / 2 - 1;
+ # Sparse hash of matching characters, keyed by index.
my %m;
+ # Loop through the first array.
+ # This should possibly be the longest array instead.
for @si.pairs -> $i {
+ # Set the limits for searching the second string.
my $jf = max(0, $i.key - $lim);
my $jt = min($i.key + $lim, @sj-1);
+ # Loop through the indices of the second array that are within
+ # limit of the first array,
for @sj[$jf..$jt] -> $j {
if $j eq $i.value && !(%m{$i.key}) {
+ # and add the first matching character in the first array to
+ # map of matches where that index is not already present.
%m{$i.key} = $i.value;
last;
}
}
}
+ # Were there any matching characters? Bag them.
if my $m = %m.values.Bag {
+ # Number of transpositions starts at zero.
my $t = 0;
+ # Disposable iterator of matching characters, in the order they
+ # appear in the second string.
my @sij = @sj.grep(* ∈ $m);
+ # Try to match up the sequences of matching characters in si and
+ # sj; any out of sequence characters increment $t
for @si.grep(* ∈ $m).pairs {
$t++ while @sij && @sij.shift ne .value;
}
- $t /= 2;
+ $t /= 2; # Jaro formula takes into account that transpositions are doubled.
- ($m / @si + $m / @sj + 1 - $t / $m) / 3
+ # Apply the formula
+ ($m / @si + $m / @sj + 1 - $t / $m) / 3
} else { 0 }
}
#| Jaro-Winkler similarity
sub sim-w (Str $a, Str $b, Rat :$p = 1/10) {
+ # Only proceed if the Jaro similarity ≠ 0;
if my $simj = sim-j($a,$b) {
+ # Determine the matching prefix length. I wanted to use
+ # Array:D.reduce for this, but it did not like last.
my $l = 0;
for ($a.comb Z $b.comb)[^4] -> [$ai, $bi] {
$ai eq $bi or last;
$l++;
}
+
+ # Apply the Winkler forumla;
$simj + $l * $p * (1 - $simj);
} else { 0 }
}
+# Jaro-Winkler distance is the 1 minus the Jaro-Winkler similarity.
say 1 - sim-w(|@*ARGS[^2]);