Challenge: The Look-and-Say Sequence
Gleichniszahlenreihe, or the look-and-say sequence, is constructed from an aural description of a sequence of numbers.
Consider the sequence of numbers that begins with 1, 11, 21, 1211, 111221, 312211, 13112221, ...
. Each number in the sequence represents what would result if the digits in the preceding value were counted and spoken aloud. For instance, "1" yields "one 1 → 11"; "11" yields "two 1s → 21"; "21" yields "one 2, one 1 → 1211", and so forth. The next number in the sequence after "13112221" is thus "one 1, one 3, two 1s, three 2s, one 1 → 1113213211".
This is a fairly complicated program. You need a few parts: the ability to take a tape and parse it into components, the ability to count components, and the ability to produce a new tape. Then a recursing bit to produce a list of these values and (ultimately) return the last one. Think about the Caesar cipher's structure.
- Compose a
%say
generator which carries out the look-and-say sequence calculation for a given input. The input should be a number which indicates which value in the sequence is desired (e.g. 1→1, 2→11, 3→21).
Solutions
These solutions were submitted by the Urbit community as part of the Hoon School Live ~2022.2 cohort. They are made available under both the MIT license and the CC0 license. We ask you to acknowledge authorship should you utilize these elsewhere.
Solution #1
This solution was produced by ~midsum-salrux. This code exhibits good core structure and code encapsulation in arms.
/gen/look-and-say.hoon
:- %say|= [* [n=@ud ~] *]:- %noun=< (compute-sequence n)|%+$ counted-digit [count=@ud digit=@t]++ compute-sequence|= n=@ud^- tape=/ sequence "1"|-?: =(n 1)sequence$(sequence (progress sequence), n (dec n))++ progress|= sequence=tape^- tape(speak (count-digits sequence))++ speak|= cd=(list counted-digit)^- tape(zing (turn cd |=(d=counted-digit ~[(crip ~(rud at count.d)) digit.d])))++ count-digits|= sequence=tape^- (list counted-digit)(scan sequence several-repeated-digits)++ several-repeated-digits (plus (cook unreap many-same-digit))++ unreap|= a=tape^- counted-digit[(lent a) (snag 0 a)]++ many-same-digit;~ pose(many-particular-digit '1')(many-particular-digit '2')(many-particular-digit '3')(many-particular-digit '4')(many-particular-digit '5')(many-particular-digit '6')(many-particular-digit '7')(many-particular-digit '8')(many-particular-digit '9')==++ many-particular-digit (corl plus just)--
Usage:
> +look-and-say 1"1"> +look-and-say 2"11"> +look-and-say 5"111221"> +look-and-say 10"13211311123113112211"> +look-and-say 20"11131221131211132221232112111312111213111213211231132132211211131221131211221321123113213221123113112221131112311332211211131221131211132211121312211231131112311211232221121321132132211331121321231231121113112221121321133112132112312321123113112221121113122113121113123112112322111213211322211312113211"
Solution #2
This solution was produced by ~nallux-dozryl. This code exemplifies parsimonious use of parsing rules and can parse any arbitrary sequence of digits.
/gen/look-and-say.hoon
:- %say|= [* [in=tape ~] ~]:- %noun^- tape=| final=tape|-?~ in final=+ nums=`tape`(scan in (star nud))=+ slot=(head nums)=+ parsed=((star (just slot)) [[1 1] nums])=+ count=(scow %ud (dec (tail (head (tail (need (tail parsed)))))))=+ return=:(weld final count (trip slot))=+ newin=(tail (tail (need (tail parsed))))$(final return, in newin)
Usage:
> +look-and-say "12""1112"> +look-and-say "123""111213"> +look-and-say "1234""11121314"> +look-and-say "123455""1112131425"