Ввод элементов неизвестного типа в вектор

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

Напишите программу на C++, реализующую сортировку списка элементов. Элементы могут быть любого типа, но все они будут одного типа, например, все целые числа, все числа с плавающей запятой, все символы или все строки (строки должны быть отсортированы, как в словаре). Вы можете реализовать любой алгоритм сортировки по вашему выбору.

  1. Спросите пользователя, сколько там будет элементов
  2. Попросите пользователя ввести элементы
  3. Попросите пользователя выбрать порядок сортировки: по возрастанию, по убыванию или по обоим.
  4. Печатать как входные, так и выходные списки
  5. Пользователь не будет предоставлять никакой информации о типе элементов

Я не очень хорошо знаком с векторами (учитель в основном бегло просматривал тему в классе), и моя книга не дает мне много информации по этому вопросу. Проблема, с которой я сталкиваюсь, заключается в том, что я не знаю тип списка элементов, пока пользователь не начнет ввод. До сих пор я пробовал:

  • создание вектора типа void (очевидно, это не разрешено сейчас, когда я его изучил, упс)
  • перегружая функцию с именем insertInVector, отправляя первый элемент в функцию и позволяя функции определить, какой тип вектора создать на основе типа первого элемента (что казалось моим лучшим вариантом, когда я думал об этом, за исключением того, что мне нужен доступ к вектор после завершения функции, так что это тоже не помогло)
  • #include <typeinfo> в программе, найти тип первого элемента, а затем создать вектор, используя vector<typeid(firstElement).name()>, и, честно говоря, я не уверен, почему это не сработало, но это не сработало.

Как я уже сказал, у меня ЧРЕЗВЫЧАЙНО ограниченный опыт работы с векторами, так как я использую их впервые. Я также довольно новый программист, поэтому многие исследования, которые я провел по этому вопросу, прошли мимо моей головы. Любая помощь, которая может быть оказана в этом, будет БОЛЬШИМ образом оценена!


person Monique    schedule 10.06.2012    source источник
comment
Я думаю, вы могли бы использовать boost::any. Аргумент шаблона необходимо передать во время компиляции.   -  person chris    schedule 10.06.2012
comment
все элементы одного типа или пользователь может вставлять элементы разных типов во время выполнения одной и той же программы?   -  person juanchopanza    schedule 10.06.2012
comment
Все элементы должны быть одного типа при вводе пользователем.   -  person Monique    schedule 10.06.2012
comment
И как вы определяете, какой тип основан на вводе?   -  person juanchopanza    schedule 10.06.2012
comment
vector<void> нельзя... vector<void*> можно. И на самом деле все требования кажутся немного странными. Вы уверены, что это лучший подход? И это домашнее задание?   -  person Zaid Amir    schedule 10.06.2012
comment
Да, это домашнее задание. Я не уверен, что это лучший подход, нет. Мой учитель склонен давать очень общие инструкции по выполнению заданий.   -  person Monique    schedule 10.06.2012
comment
Нашими инструкциями были: Напишите программу на C++ для реализации сортировки списка элементов. Элементы могут быть любого типа, но все они будут одного типа, например, все целые числа, все числа с плавающей запятой, все символы или все строки (строки должны быть отсортированы, как в словаре). Вы можете реализовать любой алгоритм сортировки по вашему выбору. 1. Спросите пользователя, сколько элементов будет там 2. Попросите пользователя ввести элементы 3. Попросите пользователя выбрать порядок сортировки: по возрастанию или по убыванию, или и то, и другое 4. Распечатайте как входные, так и выходные списки 5. Пользователь не будет предоставлять какие-либо информация о типе элементов   -  person Monique    schedule 10.06.2012
comment
Yes, this is homework. Тогда вам следует добавить тег домашнего задания к вашему вопросу   -  person Zaid Amir    schedule 10.06.2012
comment
Извини, я не знал, что это имеет значение... совсем. Моя проблема не просто в выполнении домашнего задания, а в понимании концепций, лежащих в основе моей домашней работы.   -  person Monique    schedule 10.06.2012
comment
@Monique: в целом важно, как мы ответим на вопрос. В качестве домашнего задания вы, скорее всего, получите подсказки (чтобы вы могли разобраться во всем самостоятельно) и более подробную информацию об основных понятиях (потому что мы не предполагаем, что вы уже много знаете).   -  person Matthieu M.    schedule 10.06.2012


Ответы (4)


C++ — это язык статически типизированный. Это означает, что все типы должны быть определены при компиляции: вы не можете вводить новые типы при запуске программы.

  • создание вектора типа void (очевидно, это не разрешено сейчас, когда я его изучил, упс)

void на самом деле довольно странный тип, в основном заполнитель, когда вы ожидаете тип (например, тип возвращаемого значения функции) и не можете его предоставить. void* используется в качестве указателя на неизвестный тип (в основном в C), но это настоящий хак, потому что информация об оригинале отбрасывается (насколько это касается языка), поэтому это вызывает проблемы с фактическими действиями со значением так получается.

  • перегружая функцию с именем insertInVector, отправляя первый элемент в функцию и позволяя функции определить, какой тип вектора создать на основе типа первого элемента

  • #include <typeinfo> в программе, найти тип первого элемента, а затем создать вектор, используя vector<typeid(firstElement).name()>, и, честно говоря, я не уверен, почему это не сработало, но это не сработало.

К сожалению, ни то, ни другое невозможно: поскольку вы не можете объявить переменную без типа, каким должен быть тип firstElement для начала?


Проблема, которую вы описываете, вообще сложна. В основном это означает, что вам придется принять строку символов, а затем закодировать набор правил, чтобы определить, как интерпретировать эти символы. Обычно это делается с помощью грамматики для кодирования этих правил; но грамматика может быть сложной для того, что, вероятно, является простой задачей.

Позвольте мне собрать небольшой пример:

class Input {
public:
    enum Type {
        Int,
        Double,
        String
    };

    static Input Parse(std::string const& s);

    Input(): _type(Int), _int(0), _double(0.0) {} // need to define a default...

    Type type() const { return _type; }

    int asInt() const {
        assert(_type == Int && "not an int");
        return _int;
    }

    double asDouble() const {
        assert(_type == Double && "not a double");
        return _double;
    }

    std::string const& asString() const {
        assert(_type == String && "not a string");
        return _string; 
    }

private:
    Type _type;
    int _int;
    double _double;
    std::string _string;
};

Очевидно, что настоящая проблема состоит в том, чтобы правильно Parse ввести данные.

Идея состоит в том, чтобы использовать набор правил, например:

  • int состоит исключительно из цифр, возможно с префиксом -
  • double состоит исключительно из цифр, не более чем с одним . и необязательно с префиксом -
  • string может быть чем угодно, поэтому это наш универсальный

Затем мы можем написать часть распознавания метода Parse:

static bool isInt(std::string const& s) {
    if (s.empty()) { return false; }
    
    // The first character may be among digits and '-'
    char const first = s.at(0);
    if (not isdigit(first) and first != '-') { return false; }

    // Subsequent characters may only be digits
    for (char c: s.substr(1)) {
        if (not isdigit(c)) { return false; }
    }

    // Looks like it is an int :)
    return true;
} // isInt

// Note: any int could be interpreted as a double too
static bool maybeDouble(std::string const& s) {
    if (s.empty()) { return false; }

    // The first character may be among digits, '.' and '-'
    char const first = s.at(0);
    if (not isdigit(first) and first != '.' and first != '-') { return false; }

    // There may only be one dot
    bool hasSeenDot = s.at(0) == '.';

    // Subsequent characters may only be digits and a dot now
    for (char c: s.substr(1)) {
        if (not isdigit(c) and c != '.') { return false; }

        if (c == '.') {
            if (hasSeenDot) { return false; } // no second dot allowed
            hasSeenDot = true;
        }
    }

    // Looks like it could be a double
    return true;
} // maybeDouble

static Input::Type guessType(std::string const& s) {
    if (isInt(s)) { return Input::Int; }

    // Test double after we ensured it was not an int
    if (maybeDouble(s)) { return Input::Double; }

    return Input::String;
} // guessType

И с логикой угадывания вместе, наконец, приходит синтаксический анализ:

Input Input::Parse(std::string const& s) {
    Input result;

    result._type = guessType(s);

    switch(result._type) {
    case Input::Int: {
        std::istringstream stream(s);
        s >> result._int;
        return result;
    }
    case Input::Double: {
        std::istringstream stream(s);
        s >> result._double;
        return result;
    }
    case Input::String:
        result._string = s;
        return result;
    }

    // Unreachable (normally)
    abort();
} // Input::Parse

Фу!

Так ? Почти готово. Теперь нам нужно определить, как сравнивать два входа. Это легко, если все они имеют один и тот же тип, в противном случае вам нужно будет определить произвольную логику. Вы можете достаточно легко преобразовать ввод Int во вход Double, но для строки это немного более странно.

// define < for comparing two instance of "Input",
// assuming they both have the same type
bool operator<(Input const& left, Input const& right) {
    assert(left.type() == right.type() && "Different Types!");

    switch(left.type()) {
    case Input::Int: return left.asInt() < right.asInt();
    case Input::Double: return left.asDouble() < right.asDouble();
    case Input::String: return left.asString() < right.asString();
    }
} // operator<

И, наконец, программа:

int main(int argc, char* argv[]) {
    // parse command line
    std::vector<Input> inputs;

    // by convention argv[0] is the program name, it does not count!
    for (int i = 1; i != argc; ++i) {
        inputs.push_back(Input::Parse(argv[i]));

        // Detect that the type is the same as the first input
        if (inputs.size() >= 2) {
            if (inputs.back().type() != inputs.front().type()) {
                std::cerr << "Please only use one type among Int, Double and String\n";
                return 1; // non-0 is an error
            }
        }
    }

    // sort
    std::sort(inputs.begin(), inputs.end());

    // echo back to the user
    for (Input const& i: inputs) {
        switch(i.type()) {
        case Input::Int: std::cout << i.asInt() << "\n"; break;
        case Input::Double: std::cout << i.asDouble() << "\n"; break;
        case Input::String: std::cout << i.asString() << "\n"; break;
        }
    }

    // End of the program
    return 0;
}

Конечно, поскольку я не знаю, с какими типами вы хотите иметь дело. Я выбрал произвольный набор ;) Однако это должно дать вам скелет, на котором можно основываться.

person Matthieu M.    schedule 10.06.2012
comment
Это отличный ответ. Единственное, что мне не нравится в этом дизайне, это то, что он немного прожорлив с точки зрения требований к памяти, но я думаю, что в ее случае это не имеет значения. - person akappa; 10.06.2012
comment
Это отличный ответ, но, по-видимому, смысл задания заключался в реализации алгоритма сортировки, поэтому я бы лично удалил рекомендацию использовать std::sort. Тем не менее +1 за детали (и это действительно решает проблемы ОП). - person Fraser; 10.06.2012
comment
Никогда не слышали о boost::variant? Это было бы намного лучше, чем писать свои собственные. Кроме того, вы забыли операторы break на коммутаторе. - person Puppy; 13.06.2012
comment
Это кажется излишним, поскольку единственное место, где вам вообще нужны типы, — это функция сравнения. - person Mooing Duck; 13.06.2012
comment
@DeadMG: Спасибо за break. Что касается boost::variant => боюсь, не подходит для уровня OP; мы говорим о новичке, который все еще борется с концепциями статической типизации. - person Matthieu M.; 13.06.2012
comment
@Matthieu: Именно поэтому ему нужно начинать с хороших вещей, а не с хакерских ручных работ. - person Puppy; 13.06.2012
comment
@DeadMG: тогда нам придется согласиться или не согласиться. Я думаю, что boost::variant и концепция статического посещения слишком сложны (слишком шаблонны) для стоящей перед нами задачи. - person Matthieu M.; 13.06.2012

Глядя на фактические требования проблемы, как указано в комментариях, я предлагаю вам сохранить все входные данные в std::vector<std::string> и отсортировать вектор, используя std::sort. Таким образом, вместо того, чтобы беспокоиться о разных типах, вы можете указать логику сортировки в зависимости от того, что вы интерпретируете для представления строк в своем векторе. Так

  1. Реализовать функции сортировки для строк в зависимости от того, что они представляют (подробнее позже)
  2. хранить входные данные как строки в векторе.
  3. Определите, какой тип представляют строки
  4. выберите функцию сортировки на основе этого типа
  5. Отсортируйте вектор, используя std::sort и соответствующую функцию сортировки.

Что касается функции сортировки, std::sort принимает двоичный функтор или функцию, которая применяет сравнение «меньше чем» к двум элементам, поэтому ваши функторы или функции должны выглядеть примерно так:

bool foo(const std::string& rhs, const std::string& lhs) {
  // implement the logic
}

Правка. Глядя на более свежие комментарии, кажется, что основной целью этого упражнения могла быть реализация алгоритмов сортировки для разных типов. В этом случае я бы предложил следовать подходу, используемому стандартной библиотекой C++, то есть реализовать сортировку в терминах или сравнение меньше чем между двумя типами, тем самым отделив логику сортировки от типов. быть отсортированным. Таким образом, вам понадобится функция сортировки шаблонов, созданная по шаблону типа итератора и функции/функтора сравнения.

person juanchopanza    schedule 10.06.2012
comment
лексикографический порядок отличается от числового порядка. - person akappa; 10.06.2012
comment
@akappa можно указать логику сортировки. - person juanchopanza; 10.06.2012
comment
@juanchopanza Разве вы не думаете, что вы можете реализовать любой алгоритм сортировки по вашему выбору, подразумевает, что они должны написать свой собственный алгоритм, а не использовать std::sort? - person Fraser; 10.06.2012
comment
@ Фрейзер, наверное. Я изо всех сил пытаюсь определить истинный смысл упражнения. - person juanchopanza; 10.06.2012
comment
@Fraser: или они могут указать третий аргумент при вызове std::sort. - person Matthieu M.; 10.06.2012
comment
@juanchopanza Да, я немного подозреваю, что учитель имел в виду реализацию алгоритма сортировки, но непреднамеренно сделал самую сложную часть создания контейнера. - person Fraser; 10.06.2012
comment
@Fraser да, реализация функции сортировки - одно из тех классических заданий CS. - person juanchopanza; 10.06.2012
comment
@MatthieuM. Это считается реализацией алгоритма сортировки? Думаю, это зависит от интерпретации учителя. - person Fraser; 10.06.2012
comment
Да, вы совершенно правы, смысл упражнения заключался в реализации алгоритма сортировки -_- к сожалению, он не имеет опыта преподавания (аспирант) и не углублялся в векторы, потому что они на самом деле не должны быть покрыты до более поздней главы ... до сих пор они были просто кратко представлены, но я не думаю, что он понимает это. - person Monique; 10.06.2012
comment
@Monique В этом случае я бы порекомендовал либо этот ответ, либо ответ MatthieuM, но заменив std::sort вашим собственным алгоритмом сортировки. - person Fraser; 10.06.2012
comment
@Monique: так он переплел алгоритмическую задачу с задачей дизайна (и довольно продвинутый в этом отношении)? Ему нужно запретить преподавать. - person akappa; 10.06.2012
comment
@akappa: или, возможно, просто показали лучший способ, можно было бы надеяться, что настоящий учитель разработает курсы, которые будут охвачены :/ - person Matthieu M.; 10.06.2012

Если вы знаете, какие типы может вводить пользователь, вы можете использовать шаблоны и наследование:

class Generic {
public:
  virtual void process_input() = 0; // Handles the next input from user
  virtual void process_output() = 0; // Processes the data inserted
};

template <typename T>
class HandleInput : public Generic {
private:
    std::vector<T> storage;
public:
    HandleInput(T first)
    {
      storage.push_back(first);
    }

    void process_input()
    {
      // do whatever you want
    }

    void process_output()
    {
      // do whatever you want
    }
};

int main(int argc, char **argv)
{
  // Get first input
  Input i = input();
  Generic *g;

  // Instantiate the "right" generic with a switch
  switch (i.type) {
    case T:
      g = new HandleInput<T>(i.value);
  }

  // Use Generic from here onwards
}

Это просто идея (Input не может быть реализована так, вам нужно изменить ту часть с логикой, которая получает что-то от пользователя и определяет его тип), но у нее есть преимущество маскировки типа в общий класс, поэтому вы можете разложите свой код вокруг интерфейса, предоставляемого Generic.

Другая идея (вероятно, более простая) заключается в использовании std::vector<void*> и enum, которые сообщают вам тип данных, хранящихся в векторе. Когда вам нужно будет обработать эти данные где-то в будущем, вы можете включить перечисление, чтобы соответствующим образом привести элементы вектора к правильному типу и отправить их в соответствующий код.

EDIT: еще одна идея состоит в том, чтобы определить шаблонную функцию, которая принимает входные данные и сортирует массив, используя стандартные компараторы:

#include <iostream>

#include <vector>
#include <algorithm>
#include <boost/lexical_cast.hpp>

template <typename T>
void print_v(std::vector<T> &v)
{
    typename std::vector<T>::iterator it;
    for (it = v.begin(); it != v.end(); it++)
        std::cout << *it << " ";
    std::cout << std::endl;
}

template <typename T>
void sort_and_print(T first, size_t n, bool asc)
{
    std::vector<T> v;
    v.push_back(first);
    for (size_t i = 0; i < n; i++) {
        std::string s;
        std::cin >> s;
        T e = boost::lexical_cast<T>(s);
        v.push_back(e);
    }

    print_v(v);
    if (asc)
        std::sort(v.begin(), v.end(), std::greater<T>());
    else
        std::sort(v.begin(), v.end());
    print_v(v);
}

int main(int argc, char **argv)
{
    std::string s = "test";
    sort_and_print(s, 2, true);
    unsigned int j = 3;
    sort_and_print(j, 2, true);
    return 0;
}

Логика определения типа первого ввода зависит от вас (возможно, вы можете открыть другой вопрос);)

person akappa    schedule 10.06.2012
comment
Я боюсь, что вы заходите слишком далеко от того, что понимает ОП. Он еще не внедрил идею статической типизации, а вы уже в шаблонах. (Не сказать, что ответ слишком плохой, просто я боюсь, что он за пределами его понимания) - person Matthieu M.; 10.06.2012
comment
@MatthieuM.: может быть, но я не знаю, как она могла придумать что-то более простое, не связанное с boost, или что-то более сложное (за исключением использования вектора void*, может быть, но я не уверен, что потеря типа может привести к чему-то полезному). - person akappa; 10.06.2012
comment
@akappa, похоже, это может сработать, но Матье прав, это немного выше моего понимания. Одна из основных проблем, с которой я сталкиваюсь, также заключается в том, что я не знаю, как определить тип ввода, который я получаю от пользователя. Я знаю, как использовать такие функции, как isdigit, чтобы различать символ и число, но кроме этого я ничего не знаю =\ - person Monique; 10.06.2012
comment
@Monique: ваши попытки абстрагироваться от деталей прекрасны и действительно являются хорошей практикой, но я подозреваю, что подробности о том, какие типы вы можете получить из ввода и как вы должны различать их, могут быть действительно полезными. - person akappa; 10.06.2012
comment
Но как работает переключатель? Кажется, вы используете тип для случая, который требует постоянного выражения. - person juanchopanza; 10.06.2012
comment
Я согласен, дело в том, что я не пытаюсь абстрагироваться от деталей, я просто не знаю, как НАЙТИ детали. Мне не разрешено запрашивать у пользователя ввод данных о типах, и я затрудняюсь их различить. - person Monique; 10.06.2012
comment
@Monique: ты что-нибудь знаешь о шаблонах? - person akappa; 10.06.2012
comment
@juanchopanza: прочитайте комментарии под кодом, это просто своего рода псевдокод, показывающий идею - person akappa; 10.06.2012
comment
@akappa, не так много. Я нахожусь в той же лодке с шаблонами, что и с векторами — мой учитель преподавал основную концепцию, но не более того. Я также из тех людей, которые очень хорошо учатся на примерах и работают напрямую с материалом, но мне не очень легко учиться, просто читая книгу и т. д. Этот класс для меня своего рода кошмар. - person Monique; 10.06.2012
comment
@Monique: хорошо, здесь есть два подхода: (1) не обращать внимания на тип при вставке в вектор, но учитывать его при сортировке (например, решение juanchopanza) или (2) определять тип при первом вводе и напишите шаблонную функцию с правильным типом T, который вы будете использовать в определении вектора (после этого вы можете использовать простой std::sort(v.begin(), v.end(), std::greater / std::lesser) (2) теоретически предпочтительнее (вы делаете преобразование данных только один раз, а не O(log n) раз для каждого элемента в среднем), но, вероятно, немного сложнее. - person akappa; 10.06.2012
comment
Я бы предпочел метод 2, так как мой учитель упомянул в классе, что наша программа должна уметь различать типы, но как определить тип при первом вводе?? и как это связано с шаблонами? кажется, что если бы я смог определить тип при первом вводе, после этого первого ввода я мог бы просто создать вектор с этим типом =\ - person Monique; 10.06.2012
comment
@Monique: бит синтаксического анализа объясняется в отличном ответе Матье (вы можете использовать его функцию guessType() при первом вводе, а затем вызывать правильную шаблонную функцию с переключателем при возврате функции). Вы можете увидеть, как шаблоны связаны с дифференциацией типов в коде, который я приложил в обновлении моего ответа. - person akappa; 10.06.2012
comment
@akappa ДА, то редактирование, которое вы сделали, это то, что я в основном понимаю, оно идеально: D вы не возражаете, если я просто задам пару вопросов об этом в чате с вами? Я уже достаточно засоряю комментарии, я чувствую - person Monique; 10.06.2012
comment
на самом деле nvm, мне нужно идти на работу ›.‹ большое спасибо за вашу помощь, всем, я очень ценю это - person Monique; 10.06.2012

У этого вопроса есть два аспекта: разбор и сортировка.

  • Вы можете использовать регулярные выражения для проверки типов данных, вводимых пользователем.
  • Вы можете использовать cin для анализа данных.

Во-первых: осознайте, что вы не можете обязательно знать тип ввода ваших пользователей, пока вы не получите все это ~ например: рассмотрите список пользователей имена :

728278243
390349346
495045594
elizabeth

Следовательно, лучше не предполагать, что вы лучше всех разбираетесь в входящих данных (может привести к разочарованию пользователей), а вместо этого предпочитаете рассматривать все как потенциальную строку. Храните все необработанные входные данные в виде строк, чтобы вы могли выводить их в том же формате, что и входные данные. вы можете использовать, скажем, перечисляемый тип для переключения внутри компаратора сортировки или рассмотреть возможность использования mutliset/multimap. здесь вы будете создавать упорядоченный набор. так что сортировать не надо. NB: сложность построения упорядоченного набора из N элементов или для одной сортировки N несортированных элементов списка примерно эквивалентна ~> NlogN

Для вашей задачи это вряд ли имеет значение, но на самом деле, в зависимости от того, как используется список, тот или иной подход будет гораздо более подходящим с точки зрения производительности.

Если вы уже использовали подобные std::vector, то std::multimap не должно быть слишком страшно. Грубо говоря, это связанный массив пар ключ-значение. multi здесь означает, что он может хранить несколько элементов с одним и тем же ключом (который здесь вам нужен).


В этом примере я использую библиотеку регулярных выражений boost для определения некоторых причудливых входных типов данных.
(например: sudo apt-get install libboost-regex1.46-dev)

Это регулярное выражение может показаться загадочным, но в Интернете есть много примеров практически для каждого мыслимого шаблона. [Примечание: регулярное выражение C++11 в значительной степени является заменой для повышения регулярного выражения. то есть: регулярное выражение boost должно быть совместимо с новым стандартом C++11]


blah.cpp:

#include <iostream>
#include <sstream>
#include <string>
#include <list>
#include <map>
#include <set>
#include <boost/regex.hpp>    
//NB: GNU gcc added *experimental support for regular expressions in TR1 v 4.3.0.
//    compile with:  -std=c++0x

using namespace std;
using namespace boost;

//some example input data-types (perhaps notably missing a date!) 
const regex re_char("[^0-9]", regex_constants::extended); //non numeric chars
const regex re_digit("[[:digit:]]+", regex_constants::extended); //a string of only digits in range [0..9] ~ie: Z+
const regex re_xdigit("0[xX][[:xdigit:]]+", regex_constants::extended); //support hex iff starts with '0x' or '0X'
const regex re_float("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?", regex_constants::extended); //all kinds of numbers


int main(int argc, char** argv)
{    
    int i, countc=0;
    double d;
    string str;
    int element_count;    

    do
    {
        cout << "how many elements will there be? "; 
        if (cin >> element_count) break;
        cin.clear();
        cin >> str;
        cout << "\033[A\033[2K" << flush;
    }
    while(13);
    cin.ignore(128,'\n'); 

    multimap<double, string> list_num; 
    multimap<double, string> list_fp; 
    //NB: below, by way of example, construction using the 'greater<int>' comparison class achieves _descending_ order 
    multimap<int, string, greater<int> > list_int; 
    list<string> list_str; 

    for (int next=0; next < element_count; next++)
    {
        cout << "\033[A\033[2K" << flush;
        cout << "enter next element in list ["<< next+1 << "/" << element_count << "] : "; 
        getline (cin,str);

        if (regex_match(str, re_xdigit))
        {
            //see all about manipulators here:
            //http://www.cplusplus.com/reference/iostream/istream/operator%3E%3E/
            stringstream(str) >> hex >> i;            
            list_int.insert(pair<int, string>(i, str)); 
            list_num.insert(pair<double, string>(i, str)); 
        }
        else if (regex_match(str, re_digit))
        {
            stringstream(str) >> i;            
            list_int.insert(pair<int, string>(i, str));            
            list_num.insert(pair<double, string>(i, str)); 
        }
        else if (regex_match(str, re_float))
        {
            stringstream(str) >> d;    
            list_fp.insert(pair<double, string>(d, str));        
            list_num.insert(pair<double, string>(d, str)); 
        } 

        if (regex_match(str, re_char)) countc++;      
        list_str.push_back(str);
    }    

    cout << "\033[A\033[2K" << flush;

    cout << "input: unsorted list:" << endl;
    for (list<string>::iterator it=list_str.begin(); it!=list_str.end(); it++) 
        cout << *it << endl;

    if (list_int.size() == element_count)
    {
        cout << endl << "output: sorted list of Z+ types:" << endl;
        for (multimap<int, string>::iterator it=list_int.begin() ; it != list_int.end(); it++ )
            cout << (*it).second << endl;
    }
    else if (list_fp.size() == element_count)
    {
        cout << endl << "output: sorted list of fp types:" << endl;
        for (multimap<double, string>::iterator it=list_fp.begin() ; it != list_fp.end(); it++ )
            cout << (*it).second << endl;
    }
    else if (list_num.size() == element_count)
    {
        cout << endl << "output: sorted list of numeric types:" << endl;
        for (multimap<double, string>::iterator it=list_num.begin() ; it != list_num.end(); it++ )
            cout << (*it).second << endl;
    }
    else //output as sorted strings ~but in _descending_ order, using reverse iterator, by way of example
    {
        list_str.sort(); //but best to use list_str.sort(greater<string>()); with forward iterators
        cout << endl << "output: sorted list of " <<  (countc == element_count ? "non numeric char" : "string") << " types:" << endl;
        for (list<string>::reverse_iterator it=list_str.rbegin(); it!=list_str.rend(); ++it) 
            cout << *it << endl;        
    }   

    return 0;
}

Пример был скомпилирован и запущен на Ubuntu. Материал командной строки:

$
$ lsb_release -d
Description:    Ubuntu 11.10

$ g++ --version
g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 

$ g++ --pedantic -oblah blah.cpp -lboost_regex
$ ./blah
input: unsorted list:
4.77
2.0e+2
-.3
11
0x10

output: sorted list of numeric types:
-.3
4.77
11
0x10
2.0e+2
$


Примечание. Это пример кода:

  • Здесь можно сделать множество оптимизаций. Вам явно не нужно столько контейнеров stl, сколько я использую.
  • Я не касаюсь строго направления сортировки (но показываю пару способов, которыми это может быть достигнуто).
  • Также может быть неплохо инкапсулировать специфичные для типа функциональные возможности в объектах C++; иметь базовый класс, а затем производные классы для каждого типа, который вы хотите поддерживать, но это домашнее задание, верно? - так что, наверное, не стоит перебарщивать;)
person violet313    schedule 13.06.2012