我有一个用于循环遍历字符串的python,如果当前的char应保留或被删除,则必须决定char-by-char。
假设我有一个函数fun(character,state)
,它接收一个char和一些状态参数,如果要保留char,则返回True
,并返回一个新的state
。例如:
def fun(c,state):
if state%5==0:
return False, state+1
else:
return True, state+2
我有两个想法如何使用此函数对字符串进行迭代(顺序),但我无法根据运行时复杂性和空间使用情况来决定它们之间(或其他) 权衡:
def option1(string):
state = 0
i =0
while i<len(string):
answer, state = fun(string[i],state)
if not answer:
string = string[:i]+string[i+1:]
else:
i+=1
return string
def option2(string):
result = ''
state = 0
for c in string:
answer, state = fun(c,state)
if answer:
result+=c
return result
有没有人有更好的方法/解决方案?
编辑:一些时间结果:
在一个长字符串(67976个字符)上,结果是:
看起来像用string[:i]+string[i+1:]
消除第i个字符不是个好主意。
答案 0 :(得分:3)
不建议使用+=
进行字符串连接。
更好地使用列表并加入它。
def option2(string):
result = []
state = 0
for c in string:
answer, state = fun(c, state)
if answer:
result.append(c)
return ''.join(result)
这是一个很好的explanation为什么。
虽然在CPython中优化+=
(对于某些情况?),但在其他实现中,例如PyPy,它可能会非常慢。
答案 1 :(得分:1)
在CPython中,一个好的性能经验法则是“避免在紧密循环中调用函数”。与简单的迭代计算相比,开销往往花费大量时间。在您的情况下,“强力”方法是简单地内联遍历您的字符串的循环中的逻辑:
def option4(strng):
result = []
state = 0
for c in strng:
if state % 5 == 0:
state += 1
else:
result.append(c)
state += 2
return ''.join(result)
这可能会让人觉得有点笨拙,更加“pythonic”的方法可能是使用python生成器,它将可迭代的初始状态作为输入并产生匹配的成员。
def filtergen(iter, initstate):
state = initstate
for c in iter:
if state % 5 == 0:
state += 1
else:
state += 2
yield c
def option5(strng):
return ''.join(filtergen(strng, 0))
这减少了呼叫开销 - 您只需要与匹配元素一样多的呼叫,并且作为附带好处,使调用者无需跟踪状态。此外,您不再需要中间列表。过滤器仍然是通用的 - 它适用于任何类型的迭代,而不仅仅是字符串。
将所有这些放在一起并衡量绩效:
s = 'T0kzcZ0x8VQY8ulgFKU8D1MlIUULRsVsNZMnXbjliUEES6sEIVUzpjxlHLG59z' * 1200
def fun(c, state):
if state % 5 == 0:
return False, state + 1
else:
return True, state + 2
def option3(strng):
result = []
state = 0
for c in strng:
answer, state = fun(c, state)
if answer:
result.append(c)
return ''.join(result)
def option4(strng):
result = []
state = 0
for c in strng:
if state % 5 == 0:
state += 1
else:
result.append(c)
state += 2
return ''.join(result)
def filtergen(iter, initstate):
state = initstate
for c in iter:
if state % 5 == 0:
state += 1
else:
state += 2
yield c
def option5(strng):
return ''.join(filtergen(strng, 0))
import timeit
print("string length is " + str(len(s)))
print(timeit.timeit('option3(s)', globals=globals(), number=100))
print(timeit.timeit('option4(s)', globals=globals(), number=100))
print(timeit.timeit('option5(s)', globals=globals(), number=100))
print(option3(s) == option(4) == option5(s))
在我的小笔记本电脑上,输出看起来像这样(时间以秒为单位):
pvg /tmp ➤ python3 s.py
string length is 74400
2.488818967998668
1.3291878789968905
1.268602224998176
True
'内联逻辑'和生成器都很容易胜过天真的每元素调用方法。