如何在不使用条件语句的情况下为值应用最大和最小边界
问题:
写一个Python函数,叫做clip(lo, x, hi),这个函数的功能是:如果x小于lo,就返回lo;如果x大于hi,就返回hi;否则就返回x。对于这个问题,你可以假设lo一定小于hi。
注意,这个问题不允许使用任何条件语句。相反,你需要使用Python内置的min和max函数。你可能需要先看看min和max的使用说明,并在你的解释器中试着玩一下这两个函数,然后再开始解决这个问题。
这个函数会接收三个数字,并返回一个数字。
给出的代码:
def clip(lo, x, hi):
'''
Takes in three numbers and returns a value based on the value of x.
Returns:
- lo, when x < lo
- hi, when x > hi
- x, otherwise
'''
我添加的代码:
def clip(lo, x, hi):
'''
Takes in three numbers and returns a value based on the value of x.
Returns:
- lo, when x < lo
- hi, when x > hi
- x, otherwise
'''
if min(x, lo, hi) == x:
return lo
elif max(x, lo, hi) == x:
return hi
else:
return x
问题是:我不能使用任何条件语句。求助!
10 个回答
我不想给出完整的解决方案,但你其实不需要“检查”什么。你可以用 max(x, lo)
这个方法来得到一个值,这个值不会低于 lo
。
另外,如果一个值已经被限制在某个范围内,那么再去限制它到另一个范围是不会有影响的。所以你可以放心地把一个修正后的结果再进行另一个修正。
这里有一个解决方案,假设 lo 小于 hi。
def clip(lo, x, hi):
return max(lo, min(hi, x))
下面是每种情况的工作原理:
- 当 x 小于 lo: 如果 lo 小于 hi,那么 x 也小于 hi,所以
min(hi, x)
会返回x
,而max(lo, x)
会返回lo
。 - 当 x 大于 hi:
min(hi, x)
会返回hi
,并且 如果 lo 小于 hi,那么max(lo, hi)
也会返回hi
。 - 其他情况: x 大于 lo 并且小于 hi,所以
min(hi, x)
会返回x
,而max(lo, x)
也会返回x
。
目前你有几个选项可以选择。还有一个没提到的就是嵌套的三元表达式:
def clip(lo, x, hi):
return lo if x <= lo else hi if x >= hi else x
不过因为这个使用了明确的条件测试,所以可能不太适合原来的问题。不过,在这些选项中,这个确实有个好处,就是如果 x <= lo
,它可以快速结束(其他方法都要评估所有的比较和/或进行一到两个方法调用)。接下来我们来看看这些不同的方法在性能上表现如何,使用的是timeit(测试是在Python 3.3下进行的,所以range不会生成一个列表,而是返回一个迭代器):
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次函数调用,性能很差)
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
(评估了两个测试并为每次评估生成元组,但至少没有函数调用)
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
(生成元组并排序 - 抱歉,Gnibbler,这个是最慢的)
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
(最快的,没有函数调用,只有在 x > lo
的情况下才评估 x >= hi
)
如果你把lo的值提高到测试范围的更高位置,就能看到这种快速结束的效果:
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
(如果你想在Python 2.x下重现这些,记得把 range
换成 xrange
。)