首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >避免嵌套for循环python

避免嵌套for循环python
EN

Stack Overflow用户
提问于 2017-04-02 20:26:35
回答 4查看 23.5K关注 0票数 3

我有一个函数,它接受表达式,并将变量替换为作为输入的所有值的排列。这是我的代码,我已经测试和工作,然而,在仔细研究之后,人们说嵌套for循环是一个不好的想法,但我不确定如何使这更有效。有人能帮忙吗?谢谢。

代码语言:javascript
复制
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']))
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2017-04-02 21:36:33

前言

嵌套循环本身并不是坏事。只有当问题被使用时,它们才是坏的,而对于这些问题,已经找到了更好的算法(在输入大小的效率方面有好的和坏的)。例如,对整数列表进行排序就是这样一个问题。

分析问题

大小

在上面的例子中,您有三个列表,所有的大小都是4。这使得4*4*4= 64种可能的组合,如果a总是在b前面,b总是在c之前,那么至少需要64次迭代!

你的方法

在您的方法中,a的每个可能值有4个迭代,b的每个可能值有4个迭代,c的迭代相同,所以我们总共有4*4*4= 64次迭代。所以,事实上你的解决方案是相当好的!因为没有更快的方式听所有的组合,你的方式也是最好的。

风格

关于样式,可以说您可以通过更好的变量名和组合一些for循环来改进代码。例如:

代码语言:javascript
复制
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']))

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

Itertools

正如凯文注意到的,您也可以使用itertools生成笛卡儿积。在内部,它将与您对组合for循环所做的操作相同:

代码语言:javascript
复制
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']))
票数 20
EN

Stack Overflow用户

发布于 2017-04-02 21:55:21

以下是一些想法:

  1. 由于您的列表a、b和c是硬编码的,所以不必在每一步将每个元素强制转换为字符串
  2. 使用列表理解,它们比附加的普通的for-循环要快一些。
  3. 而不是使用.replace,而是使用.format,它在一个步骤中为您完成所有的替换
  4. 使用itertools.product组合a、b和c

有了这些,我才来到这里

代码语言:javascript
复制
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.的版本相同。

票数 6
EN

Stack Overflow用户

发布于 2017-04-03 00:45:45

嵌套循环的“问题”基本上就是级别的数量是硬编码的。您编写了3个变量的嵌套。如果你只有两个呢?如果它跳到5点呢?那么你需要对代码做一些不平凡的手术。这就是推荐itertools.product()的原因。

与此相关的是,到目前为止,所有建议都硬编码了replace()调用的数量.同样的“问题”:如果您没有确切的3个变量,则必须修改替换代码。

与其这样做,不如考虑一种更干净的替代方法。例如,假设您的输入字符串是:

代码语言:javascript
复制
s = '{b}-16+({c}-({a}+11))'

而不是:

代码语言:javascript
复制
'b-16+(c-(a+11))'

也就是说,要替换的变量用大括号括起来。然后Python可以“一次”为您做所有的替换:

代码语言:javascript
复制
>>> s.format(a=333, b=444, c=555)
'444-16+(555-(333+11))'

这也是对名字和名字编号的硬编码,但是同样的事情也可以用一个小块来完成:

代码语言:javascript
复制
>>> 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"])列表只能在顶部创建一次,甚至可以传递给函数。

您只需要一点代码就可以转换输入表达式,将变量名用大括号括起来。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43173168

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档