Исходя из опыта функционального программирования, я поражен тем, что нет обычной абстракции бинарного поиска, в которую можно добавлять пользовательские функции сравнения.
Чтобы не допустить повторения этой вещи снова или снова или использовать грубые и нечитаемые хаки ООП, я просто написал эквивалент функции Arrays.binarySearch(names, "bob", myCustomComparator);
, которую вы упомянули:
class BisectRetVal():
LOWER, HIGHER, STOP = range(3)
def generic_bisect(arr, comparator, lo=0, hi=None):
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(arr)
while lo < hi:
mid = (lo+hi)//2
if comparator(arr, mid) == BisectRetVal.STOP: return mid
elif comparator(arr, mid) == BisectRetVal.HIGHER: lo = mid+1
else: hi = mid
return lo
Это была общая часть. А вот конкретные компараторы для вашего случая:
def string_prefix_comparator_right(prefix):
def parametrized_string_prefix_comparator_right(array, mid):
if array[mid][0:len(prefix)] <= prefix:
return BisectRetVal.HIGHER
else:
return BisectRetVal.LOWER
return parametrized_string_prefix_comparator_right
def string_prefix_comparator_left(prefix):
def parametrized_string_prefix_comparator_left(array, mid):
if array[mid][0:len(prefix)] < prefix: # < is the only diff. from right
return BisectRetVal.HIGHER
else:
return BisectRetVal.LOWER
return parametrized_string_prefix_comparator_left
Вот предоставленный вами фрагмент кода, адаптированный к этой функции:
>>> names = ['adam', 'bob', 'bob', 'bob', 'bobby', 'bobert', 'chris']
>>> names.sort()
>>> leftIndex = generic_bisect(names, string_prefix_comparator_left("bob"))
>>> rightIndex = generic_bisect(names, string_prefix_comparator_right("bob"))
>>> names[leftIndex:rightIndex]
['bob', 'bob', 'bob', 'bobby', 'bobert']
Он работает без изменений как в Python 2, так и в Python 3.
Для получения дополнительной информации о том, как это работает и больше компараторов для этой вещи, проверьте эту суть: https://gist.github.com/Shnatsel/e23fcd2fe4fbbd869581