改进的Python看说序列
我想先介绍一下“看说序列”。这个序列是这样的:a = {1, 11, 21, 1211, 111221 ...
这个序列的规则是,它会检查前一个数字,然后数一数有多少个相同的数字。
1 = 一个1(所以变成 = 11)
11 = 两个1(所以变成 = 21)
21 = 一个2,一个1(所以变成 = 1211)
根据这个序列的规则,数字不能超过3,所以可以创建一个翻译表来适应这个规则。不过我觉得这样不太好,因为它没有语义,我不喜欢。
我想要的是一个脚本,它能评估给定的值,并返回一个类似“看说序列”的字符串。
而且,我希望它能超出这个限制,甚至能评估字符,这样它就能返回像1A2b41
这样的结果。
我已经尝试了好几个小时想让它工作,但逻辑出了问题,现在脑袋一片空白。
这里是一个实际上不工作的脚本(返回错误的结果),但至少可以给你一个大概念。
def seq(a):
k,last,result,a = 1,'','',str(a)
for i in range(len(a)):
if last==a[i]:k+=1
else:
result = result+str(k)+a[i]
k=1
last = a[i]
return result
3 个回答
我觉得你遇到困难的部分是因为你用了没有意义的变量名。你把问题描述得很好,也给它起了名字,但在你的函数里却没有用这个名字。
如果你把一开始的字符串想象成“look”,最后得到的字符串想象成“say”,这就是一个好的开始。result
这个名字可能还不错,但a
和k
让你感到困惑。至于last
,我觉得这个名字有点误导,因为它可以表示“之前的”或者“最后的”。
另外,Python中的for
其实是foreach
,这是有原因的——你是逐个处理“look”中的每个字符,所以在循环中要明确地这样做。
def looksay(look):
look = str(look)
prev, count, say = look[0], 1, ''
for char in look[1:]:
if char == prev:
count += 1
continue
say += str(count) + prev
prev = char
count = 1
return say + str(count) + prev
空格的使用没那么重要,但Python确实有一个标准编码风格,使用它可以提高代码的可读性。你花在理解代码上的时间越少,就能越专注于解决问题。
你可以使用 groupby
,这正是你需要的:
from itertools import groupby
def lookandsay(n):
return ''.join( str(len(list(g))) + k for k, g in groupby(n))
>>> lookandsay('1')
'11'
>>> lookandsay('1A2b41')
'111A121b1411'
>>> lookandsay(lookandsay('1A2b41'))
'311A1112111b111421'
groupby
会从一个可迭代的对象中返回连续的键和分组。这里的“键”是对每个元素计算的一个函数,如果没有指定,就用默认的身份函数(就像上面提到的那样)。而“分组”是一个迭代器——当键函数的值发生变化时,就会生成一个新的分组。所以,举个例子,根据文档的说明:
# [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
# [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
我看到你的代码有两个问题:
在计算
result
时,你用到了k
和a[i]
,但其实计数器k
不是在计算字符a[i]
,而是在计算字符last
。所以这里应该把a[i]
替换成last
(你可能在第一轮不想添加任何东西)。在循环结束后,你还需要把计数器的最后值和最后一个字符再加一次(这一步还没做),也就是说,在循环后再加一句
result = result+str(k)+last
。
总的来说,代码看起来像这样:
def seq(a):
a = str(a)
k,last,result = 1,a[0],''
for i in range(1,len(a)):
if last==a[i]:k+=1
else:
result = result+str(k)+last
k=1
last = a[i]
result = result+str(k)+last
return result