Gleichniszahlenreihe

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"