简体   繁体   English

测试在循环内不会改变的条件

[英]Testing a condition that doesn't change inside a loop

Sometimes I have to check for some condition that doesn't change inside a loop, this means that the test is evaluated in every iteration, but I think this isn't the right way. 有时我必须检查一些在循环内没有变化的条件,这意味着在每次迭代中都会对测试进行评估,但我认为这不是正确的方法。

I thought since the condition doesn't change inside the loop I should only test it only once outside the loop, but then I will have to "repeat myself" and possibly write the same loop more than once. 我认为,因为条件在循环内部没有变化,我应该只在循环外测试一次,但是我必须“重复自己”并且可能不止一次地写同一个循环。 Here's a code showing what I mean: 这是一个显示我的意思的代码:

#!/usr/bin/python

x = True      #this won't be modified  inside the loop
n = 10000000

def inside():
    for a in xrange(n):
        if x:    #test is evaluated n times
            pass
        else:
            pass

def outside():
    if x:        #test is evaluated only once
        for a in xrange(n):  
            pass
    else:
        for a in xrange(n):
            pass

if __name__ == '__main__':
    outside()
    inside()

Running cProfile on the previous code gave the following output: 在前面的代码上运行cProfile给出了以下输出:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.542    0.542    0.542    0.542 testloop.py:5(inside)
        1    0.261    0.261    0.261    0.261 testloop.py:12(outside)
        1    0.000    0.000    0.803    0.803 testloop.py:3(<module>)

This shows that obviously, testing once outside the loop gives better performance, but I had to write the same loop twice (maybe more if there were some elif s). 这表明,显然,在循环外测试一次可以提供更好的性能,但我必须写两次相同的循环(如果有一些elif s可能更多)。

I know that this performance won't matter in most cases, but I need to know what's the best way to write this kind of code. 我知道在大多数情况下这种性能无关紧要,但我需要知道编写这种代码的最佳方法是什么。 For example is there a way to tell python to only evaluate the test once ? 例如,有没有办法告诉python只评估一次测试?

Any help is appreciated, thanks. 任何帮助表示赞赏,谢谢。

EDIT: 编辑:

Actually after making some tests, I'm now convinced that the difference in performance is mainly affected by other code performed within the loops, not by the evaluation of tests. 实际上,经过一些测试后,我现在确信性能上的差异主要受到循环内执行的其他代码的影响,而不是测试的评估。 So for now I'm sticking with the first form, which is more readable, and better for debugging later. 所以现在我坚持使用第一种形式,它更具可读性,更适合以后的调试。

First, a major component of the performance difference between your examples is the time it takes to lookup a global. 首先,示例之间性能差异的一个主要组成部分是查找全局的时间。 If we capture it into a local variable: 如果我们将其捕获到局部变量中:

def inside_local():
    local_x = x
    for a in xrange(n):
        if local_x:
            pass
        else:
            pass

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.258    0.258    0.258    0.258 testloop.py:13(outside)
    1    0.314    0.314    0.314    0.314 testloop.py:21(inside_local)
    1    0.421    0.421    0.421    0.421 testloop.py:6(inside)

most of the performance difference disappears. 大多数性能差异消失了。

In general whenever you have common code you should try to encapsulate it. 通常,只要有公共代码,就应该尝试封装它。 If the branches of the if have nothing in common apart from the loop then try to encapsulate the loop iterator eg into a generator. 如果的分支if没有什么在远离环共同然后尝试将循环迭代如封装到一个发电机。

This is what I usally do in this situation. 这是我在这种情况下通常做的事情。

def inside():
    def x_true(a):
        pass

    def x_false(a):
        pass

    if x:
        fn = x_true
    else:
        fn = x_false

    for a in xrange(n):
        fn(a)

python has things like closures, lambda functions, gives first class status to functions and many many built-in functions, that really help us remove duplicate code, for example imagine you needed to apply a function to a sequence of values, you could do it this way python有像闭包,lambda函数,给函数的第一类状态和许多内置函数,这真的帮助我们删除重复的代码,例如想象你需要将函数应用于一系列值,你可以做到这一点这条路

def outside():              
    if x:        # x is a flag or it could the function itself, or ...
        fun = sum # calc the sum, using pythons, sum function
    else:
        fun = lambda values: sum(values)/float(len(values)) # calc avg using our own function

    result = fun(xrange(101))

If you give us an exact scenario we can help you optimize it. 如果您给我们一个确切的场景,我们可以帮助您优化它。

I know of no interpreted language providing support in that direction, compiled languages are likely to make the comparison only once (loop invariant optimization) but this would not help much, if the evaluation of x is simple. 我知道在这个方向上没有提供支持的解释语言,编译语言很可能只进行一次比较(循环不变优化),但如果x的评估很简单,那就无济于事。 Obviously the code to be in place of the pass statements can't be completely identical, since "if" would have no use then. 显然,代替pass语句的代码不能完全相同,因为“if”就没有用了。 Typically one would write a procedure called in both places. 通常,人们会编写在两个地方调用的过程。

def outside():
    def true_fn(a):
        pass
    def false_fn(a):
        pass

    fn = true_fn if x else false_fn
    for a in xrange(n):
        fn(a)

In your case It depends on what you want: readability or performance. 在您的情况下,这取决于您想要的:可读性或性能。

If the task you are doing is some kind of filter you also can use a list_comprehension to run the loop: 如果您正在执行的任务是某种过滤器,您还可以使用list_comprehension来运行循环:

[e for e in xrange(n) if x]

If you show a little more of your code I could suggest something. 如果你展示一些你的代码,我可以提出一些建议。

Based on your original question, that you would like to test x's value without spending a lot of system resources, you already accepted an answer involving copying the value of global x to a local variable. 根据您的原始问题,您希望在不花费大量系统资源的情况下测试x的值,您已经接受了将全局x的值复制到局部变量的答案。

Now, if returning the value of x involved a multi-step function, but you were guaranteed the result would always be the same for x, then I would considering memoizing the function. 现在,如果返回x的值涉及多步函数,但是你保证结果对于x总是相同的,那么我会考虑记忆该函数。 Here is a very nice stackoverflow link on the subject 这是一个关于这个主题的非常好的stackoverflow 链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM