Archive for December, 2019

Dollar Words

December 12, 2019

My son came home from 5th grade with an interesting assignment for finding something called dollar words.  As I understand it, you assign the values 1-26 to your letters and then look for words whose letter values sum up to 100.  (These are exactly worth a dollar if you think of the letter values as being in cents.)

Naturally, this is the sort of problem that is really easy to solve in Raku.  In fact, I think carefully explaining what the program is going to do is going to take me considerably longer than writing it in the first place did!  First, here’s the full program:

sub value($word) {
    [+] $word.lc.comb.map({ $_.ord - "a".ord + 1 });
}

my @words = "/usr/share/dict/words".IO.lines;
for @words.grep({ value($_) == 100 }) -> $dollar-word {
    say $dollar-word;
}

Breaking it down, piece by piece:

sub value($word) {
    [+] $word.lc.comb.map({ $_.ord - "a".ord + 1 });
}

This creates a subroutine called value which takes a word (in the variable $word) and determines its value under this system. Step-by-step:

  • [+] takes the sum of a list that follows it.
  • $word.lc takes the variable $word and transforms it to lower-case letters. (The value chart doesn’t care whether a letter is upper or lower case, so we simplify the problem.)
  • .comb takes the result of that and translates it to a list of letters. (It’s named that because the full version of the command combs through text looking for things that match a pattern — but if you do not specify the pattern to match, it just returns one letter at a time.)
  • .map takes a list of items and applies an operation to each of them, returning another list of items. In this case we have a list of letters and want them to be a list of numbers.
  • { $_.ord - "a".ord + 1 } is the operation. $_here is the input value, and .ord takes a letter and returns its Unicode code point, the unique number the computer assigns to each letter when it stores it internally. Because the letters a-z are stored in increasing order from 97-122, this is almost what we want. We subtract the code point for “a”, which means now we have a-z mapped to 0-25. Then we add one to get the value we want.
my @words = "/usr/share/dict/words".IO.lines;

This is how we get a list of words to check for dollar words. On a Mac or Linux computer, "/usr/share/dict/words" is a file with a complete list of default words for spell-checking. This code uses the lines routine to read a file and return a list where each line in the file becomes an item in the list — in this case, each word is on its own line, so it’s a list with lots of words. (On my Mac, it’s 235,886 words.) It stores that list in the variable @words.

for @words.grep({ value($_) == 100 }) -> $dollar-word {
    say $dollar-word;
}

And now we reach the home stretch. grep is sort of like map, but instead of applying an operation to each item in a list and returning a list of the results, grep applies an operation to each item in the list and and only returns the items from the original list whose operation returns the value True. In this case, the operation is { value($_) == 100 }, which passes the input $_ to the value subroutine we defined above and then checks to see if the result it returns equals 100. So once grep has done its job, we have a list of dollar words.

We use for to go over that list of dollar words, take each word, and (one-by-one) assign it to the variable $dollar-word, then perform the operation say $dollar-word, which prints the resulting word. So taken together this prints out the full list of words from the "/usr/share/dict/words" file which are dollar words. On my computer, this finds 2,303 dollar words.