## 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.