关于范围:python:从间隔到值的映射

Python: Mapping from intervals to values

我正在重构一个函数,该函数给定一系列隐式定义间隔的端点,检查间隔中是否包含数字,然后返回相应的(不以任何可计算的方式相关)。现在处理工作的代码是:

1
2
3
4
5
6
7
8
9
10
11
12
if p <= 100:
    return 0
elif p > 100 and p <= 300:
    return 1
elif p > 300 and p <= 500:
    return 2
elif p > 500 and p <= 800:
    return 3
elif p > 800 and p <= 1000:
    return 4
elif p > 1000:
    return 5

这是我非常可怕的,缺乏在间隔和返回值都是硬编码。当然,任何数据结构的使用都是可能的。


1
2
import bisect
bisect.bisect_left([100,300,500,800,1000], p)

这里的医生:平分


这确实很可怕。如果没有要求没有硬编码,那么应该这样写:

1
2
3
4
5
6
7
8
9
10
11
12
if p <= 100:
    return 0
elif p <= 300:
    return 1
elif p <= 500:
    return 2
elif p <= 800:
    return 3
elif p <= 1000:
    return 4
else:
    return 5

以下是创建查找函数的示例,包括线性搜索和使用二进制搜索,但没有满足硬编码要求,并且对两个表进行了一些健全性检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def make_linear_lookup(keys, values):
    assert sorted(keys) == keys
    assert len(values) == len(keys) + 1
    def f(query):
        return values[sum(1 for key in keys if query > key)]
    return f

import bisect
def make_bisect_lookup(keys, values):
    assert sorted(keys) == keys
    assert len(values) == len(keys) + 1
    def f(query):
        return values[bisect.bisect_left(keys, query)]
    return f


你可以试试这个:

1
2
3
4
5
6
7
8
9
10
def check_mapping(p):
    mapping = [(100, 0), (300, 1), (500, 2)] # Add all your values and returns here

    for check, value in mapping:
        if p <= check:
            return value

print check_mapping(12)
print check_mapping(101)
print check_mapping(303)

生产:

1
2
3
0
1
2

就像在Python中一样,有更好的方法可以做到这一点。


另一种方式…

1
2
3
4
5
6
7
def which(lst, p):
    return len([1 for el in lst if p > el])

lst = [100, 300, 500, 800, 1000]
which(lst, 2)
which(lst, 101)
which(lst, 1001)

1
2
3
4
5
6
def which_interval(endpoints, number):
    for n, endpoint in enumerate(endpoints):
        if number <= endpoint:
            return n
        previous = endpoint
    return n + 1

endpoints中将端点作为列表传递,如下所示:

1
which_interval([100, 300, 500, 800, 1000], 5)

编辑:

上面是一个线性搜索。格伦·梅纳德的答案将有更好的性能,因为它使用了二分法算法。


尝试以下方法:

1
2
3
4
5
6
7
8
d = {(None,100): 0,
    (100,200): 1,
    ...
    (1000, None): 5}
value = 300 # example value
for k,v in d.items():
    if (k[0] is None or value > k[0]) and (k[1] is None or value <= k[1]):
        return v