In this task, you will write a generator that accepts a @ud number and returns a (list tape) containing all the different strings that it could represent.
Note that 1 and 0 do not map to any letters in the phonepad. Let's crash if the input @ud contains any 1 or 0.
Let's return the list of strings sorted in alphabetical order, all in lowercase.
Recall that @ud numbers need a dot marking every three digit places if the number is higher than 999, i.e. 234.567.892 is 234,567,892.
These solutions were submitted by the Urbit community as part of a competition in ~2024.8. They are made available under the MIT License and CC0. We ask you to acknowledge authorship should you utilize these elsewhere.
Solution #1
Our style winner, a clean and well-commented solution by ~norweg-rivlex.
Solution #2
The speed winner by ~diblud-ricbet.
Unit Tests
Following a principle of test-driven development, the unit tests below allow us to check for expected behavior. To run the tests yourself, follow the instructions in the Unit Tests section.
:: phone-letters.hoon
:: convert a positive integer into a list of the possible
:: phone number mnemonics
::
|= n=@ud
^- (list tape)
::
:: mapping from phone number digits to the letters that
:: may be used for it, which letters are in alphabetic
:: order
=/ digit-letters %- my :~
['2' "abc"]
['3' "def"]
['4' "ghi"]
['5' "jkl"]
['6' "mno"]
['7' "pqrs"]
['8' "tuv"]
['9' "wxyz"]
==
::
:: get the letters for the passed digit, or die if
:: none such exist
=/ letters-for-digit
|= [digit=@t]
^- tape
(need (~(get by digit-letters) digit))
::
:: for a list of incomplete mnemonics, being constructed
:: from end to start, for a passed digit, result in a
:: new list with the new letters prepended in order
=/ prepend-all-for-digit
|= [digit=@t values=(list tape)]
^- (list tape)
?: =(digit '.') values
=/ letters (flop (letters-for-digit digit))
=| result=(list tape)
|-
^- (list tape)
?~ letters result
%= $
letters t.letters
result (weld (turn values |=(item=tape [i.letters item])) result)
==
::
:: digits of the passed number in reverse order
=/ digits=tape (flop "{<n>}")
::
:: building the result
=| result=(list tape)
::
:: main loop - get each digit in reverse order, and
:: build the list of results by adding the possible
:: letters in turn
|-
?~ digits result
=/ next
?~ result (turn (letters-for-digit i.digits) |=(d=@t ~[d]))
(prepend-all-for-digit i.digits result)
%= $
digits t.digits
result next
==
:: phone-letters.hoon
:: Convert a @ud entry into an old-school T9 keyboard into a list of possible tapes.
::
|= n=@ud
^- (list tape)
?< =(n 0)
=/ char-map
%- malt
:~
[2 `(list tape)`~["a" "b" "c"]]
[3 `(list tape)`~["d" "e" "f"]]
[4 `(list tape)`~["g" "h" "i"]]
[5 `(list tape)`~["j" "k" "l"]]
[6 `(list tape)`~["m" "n" "o"]]
[7 `(list tape)`~["p" "q" "r" "s"]]
[8 `(list tape)`~["t" "u" "v"]]
[9 `(list tape)`~["w" "x" "y" "z"]]
==
=/ output `(list tape)`~
|-
?: =(n 0)
(sort output aor)
%= $
n (div n 10)
output
?~ output
(~(got by char-map) (mod n 10))
%- zing
%+ turn output
|= existing=tape
%+ turn (~(got by char-map) (mod n 10))
|= letter=tape
%+ weld letter existing
==