car*_*shi 2 python loops for-loop nested
我有一个函数,它接受表达式并将变量替换为我用作输入的值的所有排列。这是我已经测试并可以正常工作的代码,但是,在仔细研究了SO之后,人们说嵌套的for循环是一个坏主意,但是我不确定如何提高效率。有人可以帮忙吗?谢谢。
def replaceVar(expression):
eval_list = list()
a = [1, 8, 12, 13]
b = [1, 2, 3, 4]
c = [5, 9, 2, 7]
for i in expression:
first_eval = [i.replace("a", str(j)) for j in a]
tmp = list()
for k in first_eval:
snd_eval = [k.replace("b", str(l)) for l in b]
tmp2 = list()
for m in snd_eval:
trd_eval = [m.replace("c", str(n)) for n in c]
tmp2.append(trd_eval)
tmp.append(tmp2)
eval_list.append(tmp)
print(eval_list)
return eval_list
print(replaceVar(['b-16+(c-(a+11))', 'a-(c-5)+a-b-10']))
Run Code Online (Sandbox Code Playgroud)
这里有一些想法:
由于您的列表 a、b 和 c 是硬编码的,因此将它们编码为字符串,因此您不必在每一步将每个元素转换为字符串
使用列表理解,它们比带附加的普通 for 循环快一点
使用.format而不是 .replace ,它只需一步即可为您完成所有替换
使用 itertools.product 组合 a、b 和 c
综上所述,我到达了这个
import itertools
def replaceVar(expression):
a = ['1', '8', '12', '13' ]
b = ['1', '2', '3', '4' ]
c = ['5', '9', '2', '7' ]
expression = [exp.replace('a','{0}').replace('b','{1}').replace('c','{2}')
for exp in expression] #prepare the expresion so they can be used with format
return [ exp.format(*arg) for exp in expression for arg in itertools.product(a,b,c) ]
Run Code Online (Sandbox Code Playgroud)
速度增益虽然微乎其微,但也很重要,在我的机器上,它从 148 毫秒增加到 125 毫秒
功能与RQ版本相同
嵌套循环本身并不是一件坏事。如果发现了更好的算法,这些问题只会很糟糕(对于输入大小而言,效率会更好和更糟)。例如,对整数列表进行排序就是这样的问题。
在上面的例子中,您有三个大小均为4的列表。如果a始终位于b之前,而b始终位于c之前,则这将使4 * 4 * 4 = 64种可能的组合。因此,您至少需要64次迭代!
在您的方法中,我们对a的每个可能值进行4次迭代,对b的每个可能值进行4次迭代,对c进行相同的迭代。因此,我们总共有4 * 4 * 4 = 64次迭代。因此,实际上您的解决方案非常好!由于没有更快的方法来收听所有组合,因此您的方式也是最好的方法。
关于样式,可以说您可以通过使用更好的变量名并结合一些for循环来改进代码。例如:
def replaceVar(expressions):
"""
Takes a list of expressions and returns a list of expressions with
evaluated variables.
"""
evaluatedExpressions = list()
valuesOfA = [1, 8, 12, 13]
valuesOfB = [1, 2, 3, 4]
valuesOfC = [5, 9, 2, 7]
for expression in expressions:
for valueOfA in valuesOfA:
for valueOfB in valuesOfB:
for valueOfC in valuesOfC:
newExpression = expression.\
replace('a', str(valueOfA)).\
replace('b', str(valueOfB)).\
replace('c', str(valueOfC))
evaluatedExpressions.append(newExpression)
print(evaluatedExpressions)
return evaluatedExpressions
print(replaceVar(['b-16+(c-(a+11))', 'a-(c-5)+a-b-10']))
Run Code Online (Sandbox Code Playgroud)
但是请注意,迭代次数保持不变!
正如Kevin所注意到的,您还可以itertools用来生成笛卡尔乘积。在内部,它的作用与合并的for循环相同:
import itertools
def replaceVar(expressions):
"""
Takes a list of expressions and returns a list of expressions with
evaluated variables.
"""
evaluatedExpressions = list()
valuesOfA = [1, 8, 12, 13]
valuesOfB = [1, 2, 3, 4]
valuesOfC = [5, 9, 2, 7]
for expression in expressions:
for values in itertools.product(valuesOfA, valuesOfB, valuesOfC):
valueOfA = values[0]
valueOfB = values[1]
valueOfC = values[2]
newExpression = expression.\
replace('a', str(valueOfA)).\
replace('b', str(valueOfB)).\
replace('c', str(valueOfC))
evaluatedExpressions.append(newExpression)
print(evaluatedExpressions)
return evaluatedExpressions
print(replaceVar(['b-16+(c-(a+11))', 'a-(c-5)+a-b-10']))
Run Code Online (Sandbox Code Playgroud)
嵌套循环的“问题”基本上只是层数是硬编码的。您编写了 3 个变量的嵌套。如果你只有 2 个怎么办?如果跳到5怎么办?然后你需要对代码进行不平凡的手术。这就是itertools.product()推荐的原因。
与此相关的是,迄今为止的所有建议都对调用次数进行了硬编码replace()。同样的“问题”:如果你没有恰好 3 个变量,则必须修改替换代码。
与其这样做,不如考虑一种更清洁的方法来进行替换。例如,假设您的输入字符串是:
s = '{b}-16+({c}-({a}+11))'
Run Code Online (Sandbox Code Playgroud)
代替:
'b-16+(c-(a+11))'
Run Code Online (Sandbox Code Playgroud)
也就是说,要替换的变量括在花括号中。然后 Python 可以“立即”为您完成所有替换:
>>> s.format(a=333, b=444, c=555)
'444-16+(555-(333+11))'
Run Code Online (Sandbox Code Playgroud)
这也对名称和名称数量进行了硬编码,但使用字典也可以完成同样的事情:
>>> d = dict(zip(["a", "b", "c"], (333, 444, 555)))
>>> s.format(**d)
'444-16+(555-(333+11))'
Run Code Online (Sandbox Code Playgroud)
现在,关于变量数量或其名称的任何内容都不会在调用中进行硬编码format()。
值的元组 ( (333, 444, 555)) 正是返回的类型itertools.product()。变量名列表 ( ["a", "b", "c"]) 只能在顶部创建一次,甚至可以传递给函数。
您只需要一些代码来转换输入表达式,将变量名称括在花括号中。