From d7feaa9ffb3caa4ca1333ef890b01f72faf9401c Mon Sep 17 00:00:00 2001 From: Util Date: Sun, 21 Jul 2024 15:05:13 -0500 Subject: Add TWC 278 solutions by Bruce Gray, in Raku only. --- challenge-278/bruce-gray/raku/ch-1.raku | 136 ++++++++++++++++++++++++++++++++ challenge-278/bruce-gray/raku/ch-2.raku | 45 +++++++++++ 2 files changed, 181 insertions(+) create mode 100644 challenge-278/bruce-gray/raku/ch-1.raku create mode 100644 challenge-278/bruce-gray/raku/ch-2.raku diff --git a/challenge-278/bruce-gray/raku/ch-1.raku b/challenge-278/bruce-gray/raku/ch-1.raku new file mode 100644 index 0000000000..eaf2c72a3b --- /dev/null +++ b/challenge-278/bruce-gray/raku/ch-1.raku @@ -0,0 +1,136 @@ +# Variations explored: +# The whitespace is handled by: +# `.words` +# `% \s+` in a regex +# Regex expressed as: +# Inline `/regex/` +# Extracted `my Regex...` +# Grammar +# Parsing done: +# Once, then sorted in a DS for use later +# Twice, first to get the sort key and again to remove the sort key. +# Pieces after parsing represented as: +# Positional: [ word, num ] +# Positional: [ num, word ] to use default sorting +# Named: via Hash construction +# Named: via Grammar component names +# Named: via `$` assignment +# Coercion into Str and Numeric is done: +# Explicitly with `+` and `~` +# Explicitly numified with `+` during `sort`, but implicitly stringified by call to `.join`. +# Explicitly numified with `+` during `sort`, and no stringification needed. + +# Variations deliberately not explored: +# Using an Array (indexed by the trailing number) as a DS (which would be a very efficient Radix Sort) +# Would not allow duplicated trailing numbers. +# (which don't have to be a problem if it is OK to use original order as tie-breaker) +# Would cause problems with large non-sequential trailing numbers. +# (like 'foobar10000000000'). +# Using a Hash (indexed by the trailing number) as a DS +# Would not allow duplicated trailing numbers. (as above) +# Using a Hash (indexed by the leading word) as a DS +# Would not allow duplicated words without losing the order of all but the last word. +# Regex `\w+?` for leading word +# Would not allow for multi-digit trailing ordering numbers. +# Would not allow for embedded symbols in word. +# Regex `\D+?` for leading word +# Would not allow for embedded digits in word. +# Any of those unexplored variations are valid for the task (and examples) as given; I just targeted more varied use-cases. + + +sub task1a ( $words --> Str ) { + $words ~~ / ^ ( (\S+?) (\d+) )+ % \s+ $ / + orelse die; + + return $0.sort(+*.[1])».[0].join: ' '; +} + +sub task1b ( Str $s --> Str ) { + sub matchWN ( $s ) { + $s ~~ / ^ (\S+?) (\d+) $ / orelse die; + return (+$1, ~$0); + } + + return $s.words.map(&matchWN).sort».[1].join: ' '; +} + +sub task1c ( Str $s --> Str ) { + grammar WordNums { + rule TOP { ^ + % \s+ $ } + regex WN { } + regex LeadingWord { \S+? } + regex TrailingNumber { \d+ } + } + + my $p = WordNums.parse($s) + orelse die; + + # return $p..sort( +*. )»..join: ' '; + + my @wns = $p.; + + @wns .= sort: +*.; + + return @wns»..join: ' '; +} + +sub task1d ( $words --> Str ) { + $words ~~ / ^ ( $=[\S+?] $=[\d+] )+ % \s+ $ / + orelse die; + + return $0.sort(+*.)»..join: ' '; +} + +sub task1e ( $words --> Str ) { + sub parse_word_with_trailing_num ( $word ) { + $word ~~ / ^ (\S+?) (\d+) $ / + orelse die; + + return { + LeadingWord => ~$0, + TrailingNumber => +$1, + }; + } + + my @AoH = $words.words.map(&parse_word_with_trailing_num).sort(+*.); + + return @AoH»..join: ' '; +} + +sub task1f ( $words --> Str ) { + my Regex $EndNum = / (\d+) $ /; + + return $words.words.sort({ $_ ~~ $EndNum ?? +$0 !! die })».subst($EndNum).join: ' '; +} + + +constant @tests = + ( 'and2 Raku3 cousins5 Perl1 are4' , 'Perl and Raku are cousins' ), + ( 'guest6 Python1 most4 the3 popular5 is2 language7' , 'Python is the most popular guest language' ), + ( 'Challenge3 The1 Weekly2' , 'The Weekly Challenge' ), + + # Added tests for non-sequential numbering, multi-digit numbers, + # embedded numbers, symbols, duplicated words, and duplicated order-numbers. + # https://shakespeare.mit.edu/hamlet/hamlet.2.2.html + # https://tvtropes.org/pmwiki/pmwiki.php/Recap/DragonBallZAbridgedE9 + + ( 'Challenge11 The1 Weekly2' , 'The Weekly Challenge' ), + + ( 'Words14 What2 Words16 you6 lord?12 read,8 my10 Words18 do4', 'What do you read, my lord? Words Words Words' ), + + ( 'Happiest7 9minutes3 moment9 of11 my13 18seconds5 life15', '9minutes 18seconds Happiest moment of my life' ), +; +constant @subs = + :&task1a, + :&task1b, + :&task1c, + :&task1d, + :&task1e, + :&task1f, +; +use Test; plan +@subs * +@tests; +for @subs -> ( :key($sub_name), :value(&task1) ) { + for @tests -> ( $in, $expected ) { + is task1($in), $expected, "$sub_name : $in"; + } +} diff --git a/challenge-278/bruce-gray/raku/ch-2.raku b/challenge-278/bruce-gray/raku/ch-2.raku new file mode 100644 index 0000000000..448fbb99d4 --- /dev/null +++ b/challenge-278/bruce-gray/raku/ch-2.raku @@ -0,0 +1,45 @@ +sub csj ( Str $s --> Str ) { return $s.comb.sort.join } + +sub task2a ( $str, $char --> Str ) { + return $str.subst: / ^ ( .+? $char ) /, + { csj(~$0) }; +} +sub task2b ( $str, $char --> Str ) { + my $k = $str.index($char) + orelse return $str; + + return $str.substr(0,$k+$char.chars).&csj + ~ $str.substr( $k+$char.chars); +} +sub task2c ( $str, $char --> Str ) { + my ($a, $b) = $str.split: $char, 2; + + return $b.defined ?? "$a$char".&csj ~ $b !! $str; +} + + +my @tests = + , + , + , + + # All my solutions also work with $char.chars > 1. + , + + # Corner cases of search string being at the very end. + , + , + , + , +; +my @subs = + :&task2a, + :&task2b, + :&task2c, +; +use Test; plan +@tests * +@subs; +for @subs -> ( :key($sub_name), :value(&task2) ) { + for @tests -> ( $in_str, $in_char, $expected ) { + is task2($in_str, $in_char), $expected, "$sub_name: $in_char, $in_str"; + } +} -- cgit