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

前端 未结 9 1760
滥情空心
滥情空心 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:39

    solution:

    def clip(lo, x, hi):
        x = max(lo, x)
        x = min(x, hi)   
        return x
    
    0 讨论(0)
  • 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)[x<hi])[x>lo] 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.)

    0 讨论(0)
  • 2021-02-10 11:42

    Here is a solution, assuming that lo < hi.

    def clip(lo, x, hi):
        return max(lo, min(hi, x))
    

    How it works in each case:

    • lo, when x < lo: if lo < hi, then x < hi, so min(hi, x) returns x and max(lo, x) returns lo.
    • hi, when x > hi: min(hi, x) returns hi and if lo < hi, max(lo, hi) returns hi
    • x, otherwise: x > lo and x < hi, so min(hi, x) returns x and max(lo, x) returns x
    0 讨论(0)
提交回复
热议问题