diff options
| author | Francis Whittle <FJ.Whittle@gmail.com> | 2019-06-02 11:06:14 +1000 |
|---|---|---|
| committer | Francis Whittle <FJ.Whittle@gmail.com> | 2019-06-02 11:06:37 +1000 |
| commit | 89921e6798375fa6fb6171d337dddb1c0d1929e1 (patch) | |
| tree | b1745033394d85df0e9a7d3832dc8b7eb169b5fd /challenge-010 | |
| parent | 96ef0ed3f6ba2ab8d4a5d9db6af8db3e040e00cf (diff) | |
| download | perlweeklychallenge-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.txt | 1 | ||||
| -rw-r--r-- | challenge-010/fjwhittle/perl6/ch-1.p6 | 34 | ||||
| -rw-r--r-- | challenge-010/fjwhittle/perl6/ch-2.p6 | 27 |
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]); |
