How to apply max & min boundaries to a value without using conditional statements

前端 未结 9 1778
滥情空心
滥情空心 2021-02-10 11:01

Problem:

Write a Python function, clip(lo, x, hi) that returns lo if x is less than lo; hi if x is greater than hi; and x otherwise. For this problem, y

9条回答
  •  南笙
    南笙 (楼主)
    2021-02-10 11:42

    So you have a number of options proposed so far. Not yet posted is the nested ternary expression:

    def clip(lo, x, hi):
        return lo if x <= lo else hi if x >= hi else x
    

    But since this uses explicit conditional tests, probably not suitable as a solution to the original question. Still, given these options, this is the one that actually has the advantages of short-circuiting if x <= lo (all other methods evaluate all comparisons and/or perform one or two method calls). Let's see how these alternatives actually perform using timeit (tested with Python 3.3, so range does not build a list, but returns an iterator):

    python -m timeit -s "lo,hi=10,90" "[max(lo,min(hi,x)) for x in range(100)]"
    10000 loops, best of 3: 54.5 usec per loop
    

    (2 function calls per evaluation, kills performance)

    python -m timeit -s "lo,hi=10,90" "[(lo,(hi,x)[xlo] for x in range(100)]"
    10000 loops, best of 3: 40.9 usec per loop
    

    (evaluates both tests and builds tuples for every evaluation, but at least no function calls)

    python -m timeit -s "lo,hi=10,90" "[sorted((lo,x,hi))[1] for x in range(100)]"
    10000 loops, best of 3: 90.5 usec per loop
    

    (builds tuple and sorts - sorry, Gnibbler, this is the slowest)

    python -m timeit -s "lo,hi=10,90" "[lo if x <= lo else hi if x >= hi else x for x in range(100)]"
    100000 loops, best of 3: 18.9 usec per loop
    

    (fastest, no function calls, only evaluates x >= hi if x > lo)

    This short-circuiting can be seen if you move the value of lo to much higher in the test range:

    python -m timeit -s "lo,hi=80,90" "[lo if x <= lo else hi if x >= hi else x for x in range(100)]"
    100000 loops, best of 3: 15.1 usec per loop
    

    (If you want to reproduce these under Python 2.x, replace range with xrange.)

提交回复
热议问题