Използване на std::equal_range за намиране на диапазона от префикси, които се срещат във вектор от низове

Опитвам се да измисля ламбда, която ще позволи на std::equal_range да върне диапазон, в който търсеният низ съществува като префикс. Тъй като това вероятно не е формулирано правилно, пример:

Даден е векторът от низове:

  • C:\users\andy\documents\screenshot.jpg
  • C:\users\bob\desktop\file.txt
  • C:\users\bob\desktop\picture.png
  • C:\users\bob\desktop\video.mp4
  • C:\users\john\desktop\note.txt

Бих очаквал итераторите да бъдат върнати

  • C:\users\bob\desktop\file.txt и
  • C:\users\bob\desktop\video.mp4.

Как да напиша ламбда за сравнение за std::equal_range, която постига това, или std::equal_range не е правилният инструмент за тази работа?


person Andrew LeFevre    schedule 28.12.2017    source източник
comment
Подниз или префикс? подниз може да се появи навсякъде в низа и резултатите не е необходимо да са съседни.   -  person Retired Ninja    schedule 28.12.2017
comment
Предполагам, че почти винаги ще търся префикс, добър улов.   -  person Andrew LeFevre    schedule 28.12.2017
comment
Претоварването с четири аргумента на std::equal_range с извиквания обект за сравнение изглежда достатъчно за задачата. Просто кодирайте обект за сравнение, който реализира сравнение, което съответства на префикса. Ще трябва внимателно да кодирате обекта за сравнение с отделни реализации на comp(element, value) и comp(value, element) , за да върнете правилния резултат за сравнение.   -  person Sam Varshavchik    schedule 28.12.2017
comment
Е, започнете с основите. При дадена функция, която приема пълен низ и префикс като параметър, връща стойност bool, показваща дали пълният низ се съпоставя преди префикса. Това е смешно тривиално и веднъж написано, вече сте 50% готови. Останалите 50% са функция, която приема префикс и пълен низ и връща true, ако префиксът се сравни преди пълния низ. Проблема решен.   -  person Sam Varshavchik    schedule 28.12.2017
comment
Това изглежда работи, но не съм сигурен, че е точно правилно: ideone.com/NiV02v Вероятно бихте могли да избегнете substr с помощта на std::string.compare, но това ми беше по-ясно, докато го гледах. свиване на рамене   -  person Retired Ninja    schedule 28.12.2017
comment
Бих предприел малко по-сложен подход от този на пенсионираните нинджи. Бих принудил префиксния низ в отделен помощен тип, който не е std::string. След това мога да правя разлика между двата типа функции за сравнение и да внедря две operator()s в персонализирания клас за сравнение, сравнявайки префикс към низ и префикс към низ.   -  person Sam Varshavchik    schedule 28.12.2017
comment
Да, това беше просто начин да го накарам да работи по някакъв начин. Две различни функции улесняват виждането как ще бъдат използвани с lower_bound/upper_bound.   -  person Retired Ninja    schedule 28.12.2017
comment
@RetiredNinja, който изглежда работи перфектно! Просто съм объркан защо сравняването с ‹ в инструкциите if работи и защо сравняването с ‹= не е необходимо?   -  person Andrew LeFevre    schedule 28.12.2017
comment
какво ще кажете за C:\users\bob\desktop\picture.png защо не трябва да се връща?   -  person Killzone Kid    schedule 28.12.2017
comment
Използването на <= или >= нарушава строгото изискване за слабо подреждане за сравнение. Този отговор го обяснява по-добре, отколкото бих могъл. :) stackoverflow.com/a/981299/920069   -  person Retired Ninja    schedule 28.12.2017
comment
@KillzoneKid std::equal_range връща диапазон, първия елемент, който съвпада, и последния елемент, който съвпада. Така че C:\users\bob\desktop\picture.png е в диапазона между C:\users\bob\desktop\file.txt и C:\users\bob\desktop\video.mp4.   -  person Andrew LeFevre    schedule 28.12.2017
comment
@AndrewLeFevre Разбрах!   -  person Killzone Kid    schedule 28.12.2017
comment
@SamVarshavchik Каква е ползата от твоя по-сложен подход? Ще подобри ли изобщо производителността? Просто се чудя дали вашият подход е обективно по-добър по някакъв начин.   -  person Andrew LeFevre    schedule 28.12.2017
comment
Той избягва необходимостта компараторът изрично да улавя префикса извън лентата, за да разбере коя стойност на параметъра е префиксът. Вероятно малко по-бърз, няма нужда да правите това сравнение при всяко повикване; но се съмнявам, че това ще има някаква разлика при съвременните многогигахерцови процесори.   -  person Sam Varshavchik    schedule 29.12.2017
comment
@SamVarshavchik о добре благодаря!   -  person Andrew LeFevre    schedule 29.12.2017


Отговори (1)


Мисля, че просто трябва да накарате компаратора да сравнява само дължината на префикса с елементи като този:

std::vector<std::string> v
{
    "C:/users/andy/documents/screenshot.jpg",
    "C:/users/bob/desktop/file.txt",
    "C:/users/bob/desktop/picture.png",
    "C:/users/bob/desktop/video.mp4",
    "C:/users/john/desktop/note.txt",
};

std::sort(std::begin(v), std::end(v));

std::string const prefix = "C:/users/bob/desktop/";

auto lb = std::lower_bound(std::begin(v), std::end(v), prefix);

// For the upper bound we want to view the vector's data as if
// every element was truncated to the size of the prefix.
// Then perform a normal match.
auto ub = std::upper_bound(lb, std::end(v), prefix,
[&](std::string const& s1, std::string const& s2)
{
    // compare UP TO the length of the prefix and no farther
    if(auto cmp = std::strncmp(s1.data(), s2.data(), prefix.size()))
        return cmp < 0;

    // The strings are equal to the length of the prefix so
    // behave as if they are equal. That means s1 < s2 == false
    return false;
});

// make the answer look like we used std::equal_range
// (if that's what's needed)
auto range = std::make_pair(lb, ub);

for(auto itr = range.first; itr != range.second; ++itr)
    std::cout << *itr << '\n';

Изход:

C:/users/bob/desktop/file.txt
C:/users/bob/desktop/picture.png
C:/users/bob/desktop/video.mp4

За да обясните защо това работи, представете си да вземете вектора и да го сортирате. След това си представете, че посещавате всеки елемент и го съкращавате до дължината на префикса. Ще останете с сортиран вектор без елементи, по-дълги от префикса. В този момент един прост std::equal_range ще направи това, от което се нуждаете. По този начин всичко, което трябва да направим, е да създадем компаратор, който се държи сякаш елементите на контейнера са съкратени до дължината на префикса и да използваме този компаратор в нашето std::equal_range търсене ( или близнак std::lower_bound/upper_bound търсене).

person Galik    schedule 28.12.2017
comment
Доколкото разбирам от коментарите на Сам Варшавчик по-горе, трябва да сравните, като използвате както comp(element, value), така и comp(value, element), нали? Освен това използването на string::compare ще бъде ли по-бързо тук? - person Andrew LeFevre; 29.12.2017
comment
@AndrewLeFevre Да, прав си. Това е по-сложно, отколкото си мислех. Ще го обмисля още малко. - person Galik; 29.12.2017
comment
@AndrewLeFevre Вярвам, че това вече е поправено. - person Galik; 29.12.2017