Description

There exists a sequence of numbers that follows the pattern

          1
         11
         21
        1211
       111221
       312211
      13112221
     1113213211
          .
          .
          .

Starting with “1” the following lines are produced by “saying what you see”, so that line two is “one one”, line three is “two one(s)”, line four is “one two one one”.

Write a function that given a starting value as a string, returns the appropriate sequence as a list. The starting value can have any number of digits. The termination condition is a defined by the maximum number of iterations, also supplied as an argument.

Solutions

Julia

# Look-and-say-numbers.jl


"""
执行一轮,生成对字符串连续字符计数的新字符串
"""
#> "执行一轮,生成对字符串连续字符计数的新字符串\n"
function count_string(s::String)::String
    # 解法一:遍历
    # seq = []
    # k, n = s[1], 0
    # for c ∈ s
    #     c == k && (n += 1)
    #     if c != k
    #         push!(seq, "$n$k")
    #         k = c
    #         n = 1
    #     end
    # end
    # push!(seq, "$n$k")
    # seq |> join

    #  解法二:正则
    replace(s, r"(.)\1*" => m -> string(length(m)) * m[1])
end
#> count_string (generic function with 1 method)

count_string("112223")
#> "213213"


"""
# Arguments
- `data::String`: starting string
- `n::Int`: number of turns
"""
#> "# Arguments\n- `data::String`: starting string\n- `n::Int`: number of turns\n"
function lookandsay(data::String, n::Int64)::Vector{String}
    # 利用赋值语句有返回值的特性
    [data = count_string(data) for _  1:n]

    # 否则就必须写成这样,与for循环也差不多了
    # [
    #     begin
    #         data = count_string(data)
    #         data
    #     end for _ ∈ 1:n
    # ]
end
#> lookandsay (generic function with 1 method)

lookandsay("1", 5)
#> 5-element Vector{String}:
#>  "11"
#>  "21"
#>  "1211"
#>  "111221"
#>  "312211"

R

# Look-and-say-numbers.R


library(tidyverse)
library(magrittr)


count_string <- function(s) {
    s %>%
        str_match_all(pattern = "(.)\\1*") %>%
        extract2(1) %>%
        apply(
            MARGIN = 1,
            FUN = \(row) str_c(nchar(row[1]), row[2])
            # 每行的第一个元素是匹配的全部字符,第二个是()中的字符
        ) %>%
        str_c(collapse = "")
}

count_string("112223")
#> [1] "213213"
lookandsay <- function(s, max_len) {
    1:max_len %>%
        map_chr(\(x) s <<- count_string(s)) # <<- 才能修改外部变量
    # <- 赋值表达式有返回值,可以 return()
}

lookandsay("1", 5)
#> [1] "11"     "21"     "1211"   "111221" "312211"

JavaScript

/**
 * @module Look-and-say-numbers
 */


function countString(s) {
    return s.replace(/(.)\1*/g, (match, p1) => match.length + p1);
}

console.log(countString('112223'));


function lookAndSay(s, len) {
    // 利用了变量重新赋值语句有返回值,以及匿名函数可以修改外部变量的特性
    return [...Array(len)].map(() => s = countString(s));
}

console.log(lookAndSay('1', 5));
#> 213213
#> [ '11', '21', '1211', '111221', '312211' ]

Python

# Look-and-say-numbers.py

#%%
from itertools import groupby
import re


#%%
def count_string(s):
    # 解法一:正则替换
    # `\1*`表示第一个()中的内容再重复0次以上
    # m.group(0) 是每个成功匹配的全部字符,m.group(1) 是 () 中的字符
    # 就像 R 中 stringr::str_match() 每行的两个元素
    # return re.sub(r'(.)\1*', lambda m: str(len(m.group(0))) + m.group(1), s)

    # 解法二:正则匹配
    numstrs = re.findall('1+|2+|3+|4+|5+|6+|7+|8+|9+', s)
    return "".join(map(lambda x: str(len(x)) + x[0], numstrs))

    # 解法三:groupby(),返回元组,分别是相同字符及其 itrerator
    # return "".join((str(len(list(g))) + n) for n, g in groupby(s))


count_string("112223")


#%%
#> '213213'
def look_and_say(data='1', maxlen=5):
    ''' 
    data:   starting number set
    maxlen: max sequence length
    '''

    # 解法一:使用 map 迭代
    s = [data]

    def gender_output(s):
        s[0] = count_string(s[0])
        return s[0]  # python 的赋值表达式没有返回值,所以不能 return s = 1

    return list(map(lambda t: gender_output(s), range(maxlen)))

    # 解法二:循环
    # output = []
    # for i in range(maxlen):
    #     data = count_string(data)
    #     output.append(data)
    # return output


look_and_say("1", 5)

# %%
#> ['11', '21', '1211', '111221', '312211']