避免嵌套for循环python

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)

Cop*_*eld 6

这里有一些想法:

  1. 由于您的列表 a、b 和 c 是硬编码的,因此将它们编码为字符串,因此您不必在每一步将每个元素转换为字符串

  2. 使用列表理解,它们比带附加的普通 for 循环快一点

  3. 使用.format而不是 .replace ,它只需一步即可为您完成所有替换

  4. 使用 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版本相同


R. *_* Q. 5

前言

嵌套循环本身并不是一件坏事。如果发现了更好的算法,这些问题只会很糟糕(对于输入大小而言,效率会更好和更糟)。例如,对整数列表进行排序就是这样的问题。

分析问题

规模

在上面的例子中,您有三个大小均为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)

但是请注意,迭代次数保持不变!

Itertools

正如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)

  • 我将使用tuple-unpacking删除三行代码:itertools.product(...)中的“ a,b,c” (2认同)

Tim*_*ers 5

嵌套循环的“问题”基本上只是层数是硬编码的。您编写了 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"]) 只能在顶部创建一次,甚至可以传递给函数。

您只需要一些代码来转换输入表达式,将变量名称括在花括号中。