Возвращает пустой вектор строк, если ключ не найден - PullRequest
5 голосов
/ 16 сентября 2011

Я знаю, что это очень плохая идея, поэтому другие предложения о том, как сделать это эффективно, будут хорошо приняты.

Вот в чем дело.У меня есть map<string,vector<string> >, я хочу найти ключ и вернуть его соответствующее значение (вектор строк в этом случае).Причина, по которой я настаиваю на возврате (а не просто повторении), заключается в том, что мне нужно искать значения, возвращенные в каком-то другом векторе.

Пример прояснит это:

Input:

key1 ---> {2,3,4}
key2 ---> {1}
key3 ---> {2,12,11,9}

Для ключа 1 в качестве ввода, вектор со значениями 2,3,4 должен быть возвращен.Теперь эти 2,3,4 значения нужно искать в другом векторе строк.Какой самый эффективный способ сделать это?

Я пробовал что-то вроде этого:

vector<string> returnEdges(string key)
{
    for (map<string, vector<string> >::iterator it=outgoing.begin();
    it!=outgoing.end();++it)
    {
        if (key.compare((*it).first)==0)
        {
            return (*it).second;
        }
    }


    //return string<;//what should I return here????


}

1) Как вернуть пустой вектор, если ключ не найден?

2) Каков наилучший способ реализовать это?

Надеюсь, вопрос ясен.

РЕДАКТИРОВАТЬ: Когда я писал вопрос, я подумал, почему бы не вернуть итератор?Люди в СО одобряют эту идею?

Ответы [ 4 ]

5 голосов
/ 16 сентября 2011

1) Возвращение итератора - хорошая идея.Когда вы делаете это, естественным способом указать случай «не найден» является возврат итератора .end ().Недостатком является то, что абстракция имеет некоторую утечку: вызывающая сторона должна иметь возможность получить это значение .end (), чтобы сравнить его с ним для проверки ошибок, а возвращенный итератор предоставляет более богатый интерфейс, чем выlike (клиентский код не должен поиграть с увеличением и уменьшением итератора).

2) Возврат пустого вектора так же прост, как создание пустого вектора и его возврат.Создание пустого вектора = создание пустого вектора.Это то, что вы получаете - барабанная дробь - конструктор по умолчанию для векторного класса.

3) Вам не нужно и не нужно реализовывать цикл поиска самостоятельно.Стандартная библиотека уже реализует это для вас.(Существует специальная функция find для map с из-за различия ключа / значения. Для таких последовательностей, как list, vector и deque, предпочитайте свободную функцию std::find, которая исходит от <algorithm>.

4) Вы должны предпочесть принимать параметры функции (когда они являются экземплярами классов, например std::string) и возвращать данные (особенно сложные вещи, такие как вектор строк) по константной ссылке.Передача и возврат по значению подразумевает копию;иногда компилятор может оптимизировать это, но это не так надежно, как хотелось бы.Кроме того, причина, по которой вы используете C ++ в первую очередь, заключается в том, чтобы иметь такой уровень контроля над вещами, верно?Если нет, то не мучайте себя этим.

Тем не менее, вы не сможете этого сделать, если собираетесь возвращать вновь созданное значение иногда.Еще одним способом разработки интерфейса является возвращение указателя на вектор строк (обратите внимание, что арифметика указателей на них будет недействительной) внутри карты или указатель NULL, если значение не найдено.Это позволяет избежать копирования и отличает результат «не найден» от фактического пустого вектора в данных, но это означает, что клиентскому коду приходится иметь дело с нечетким необработанным указателем.

5) Наличие «возврата» в именифункции бесполезны, так как возвращение - это то, что делают функции.OTOH, это хорошая идея, чтобы называть вещи так, чтобы было понятно, почему параметры такие, какие они есть.

6) С итераторами для сложных типов часто хорошая идея установить typedefs.

Возвращение итератора так же просто, как:

typedef map<string, vector<string> >::iterator graph_iterator;
graph_iterator edges_named(const string& node_name) {
    return outgoing.find(node_name);
}

Возвращение вектора строк так же просто, как:

typedef map<string, vector<string> >::iterator graph_iterator;
vector<string> edges_named(const string& node_name) {
    graph_iterator it = outgoing.find(node_name);
    return it == outgoing.end() ? vector<string>() : it->second;
}

Возвращение указателя так же просто, как:

typedef map<string, vector<string> >::iterator graph_iterator;
vector<string>* edges_named(const string& node_name) {
    graph_iterator it = outgoing.find(node_name);
    return it == outgoing.end() ? NULL : &(it->second);
}

Выбирай с умом.

2 голосов
/ 16 сентября 2011

Как насчет этого:

std::vector<std::string> find_by_key_maybe(const std::string & key)
{
  std::map<std::string, std::vector<std::string>>::const_iterator it = themap.find(key);
  return it == themap.end() ? std::vector<std::string>() : it->second;
}

Или даже, если themap не является константой, и вы также хотите добавить пустой вектор на карту:

std::vector<std::string> find_by_key_and_insert_if_missing(const std::string & key)
{
  return themap[key];
}

Вполне нормально возвращать вектор по значению, если этого требует ситуация.

1 голос
/ 16 сентября 2011

Большую часть времени вам не требуется возвращать копию вектора, ссылка на const также подойдет.Вы всегда можете сделать копию позже, если вам нужно.

Также вам не нужно перебирать карту, они оптимизированы для поиска методом find.

const vector<string> & returnEdges(string key)
{
    map<string, vector<string> >::iterator it = outgoing.find(key);
    if (it == outgoing.end())
    {
        static vector<string> empty_vector;
        return empty_vector;
    }
    return it->second;
}
0 голосов
/ 16 сентября 2011

Я думаю, что оператор [] может вам помочь, он вернет пустое значение (пустой вектор здесь).Так что все, что вам нужно сделать, это

vector<string> returnEdges(string key) 
{
    return outgoing[key];
}
...