我有一个函数,它接受表达式并用我用作输入的值的所有排列替换变量。这是我测试和工作的代码,但是在查看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']))
答案 0 :(得分:3)
嵌套循环本身并不是一件坏事。它们只是很糟糕,如果有问题,已经找到了更好的算法(在输入大小方面的效率方面有好有坏)。例如,对整数列表进行排序就是一个问题。
在你的情况下,你有三个列表,全部大小4.这使得4 * 4 * 4 = 64种可能的组合,如果a总是在b之前,b在b之前。所以你需要至少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']))
但请注意,迭代次数保持不变!
注意到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']))
答案 1 :(得分:3)
这里有一些想法:
因为你的列表a,b和c是硬编码的,将它们编码为字符串,因此你不必在每一步都将每个元素都转换为字符串
使用列表理解,它们比附加的正常for循环快一点
代替.replace,使用.format,它只需一步即可完成替换
使用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) ]
速度增益是微不足道的,但是在我的机器中它从148毫秒到125
功能与 R.Q。
的版本相同答案 2 :(得分:1)
因此,您当前的结构解决了itertools.product
解决方案无法解决的低效问题之一。您的代码正在保存中间替换的表达式并重用它们,而不是使用每个itertools.product
元组重做这些替换。这很好,我认为您当前的代码是有效的。
然而,它很脆弱,仅在替换正好三个变量时才有效。动态编程方法可以解决这个问题。为此,我将略微改变输入参数。该函数将使用两个输入:
expressions
- 要替换为
replacement_map
- 一个字典,提供替换每个变量的值
动态编程功能如下:
def replace_variable(expressions, replacement_map):
return [list(_replace_variable([e], replacement_map)) for e in expressions]
def _replace_variable(expressions, replacement_map):
if not replacement_map:
for e in expressions:
yield e
else:
map_copy = replacement_map.copy()
key, value_list = map_copy.popitem()
for value in value_list:
substituted = [e.replace(key, value) for e in expressions]
for e in _replace_variable(substituted, map_copy):
yield e
使用示例:
expressions = ['a+b', 'a-b']
replacement_map = {
'a': ['1', '2'],
'b': ['3', '4']
}
print replace_variable(expressions, replacement_map)
# [['1+3', '1+4', '2+3', '2+4'], ['1-3', '1-4', '2-3', '2-4']]
请注意,如果您使用的是Python 3.X,则可以使用yield from iterator
构造,而不是在e
中重复_replace_variables
两次。这个函数看起来像:
def _replace_variable(expressions, replacement_map):
if not replacement_map:
yield from expressions
else:
map_copy = replacement_map.copy()
key, value_list = map_copy.popitem()
for value in value_list:
substituted = [e.replace(key, value) for e in expressions]
yield from _replace_variable(substituted, map_copy)
答案 3 :(得分:0)
嵌套循环的“问题”基本上只是级别的数量是硬编码的。您为3个变量编写了嵌套。如果你只有2个怎么办?如果跳到5怎么办?然后你需要对代码进行非平凡的手术。这就是推荐itertools.product()
的原因。
相关地,到目前为止所有建议都硬编码replace()
次呼叫的数量。相同的“问题”:如果您没有正好3个变量,则必须修改替换代码。
不要这样做,而是考虑采用更清洁的方式进行替换。例如,假设您的输入字符串是:
s = '{b}-16+({c}-({a}+11))'
而不是:
'b-16+(c-(a+11))'
也就是说,要替换的变量用大括号括起来。然后Python可以“立刻”为你做所有的替换:
>>> s.format(a=333, b=444, c=555)
'444-16+(555-(333+11))'
这也很难对名字的名称和数量进行编码,但是用dict可以完成同样的事情:
>>> d = dict(zip(["a", "b", "c"], (333, 444, 555)))
>>> s.format(**d)
'444-16+(555-(333+11))'
现在,在format()
电话中硬编码变量或其名称的数量。
值的元组((333, 444, 555)
)正是itertools.product()
返回的那种东西。变量名称列表(["a", "b", "c"]
)只能在顶部创建一次,甚至可以传递给函数。
您只需要一些代码来转换输入表达式,将变量名称括在花括号中。