Ускоренный C++, задача 5-6 (копирование значений изнутри вектора на передний план)

Я работаю над упражнениями в Accelerated C++ и застрял на вопросе 5-6. Вот описание проблемы: (несколько сокращенно, я удалил лишнюю информацию.)

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

(students — это вектор структур student. Структуры student содержат имя и оценки отдельного учащегося.)

В частности, у меня возникли проблемы с получением функции vector.insert для правильного копирования передаваемых структур student в начало вектора students. Вот функция extract_fails в том виде, в котором она у меня есть (обратите внимание, что она еще не изменяет размер вектора, как указано в описании проблемы; это должно быть тривиально, когда я решу свою текущую проблему).

// Extract the students who failed from the "students" vector.
void extract_fails(vector<Student_info>& students)
{
    typedef vector<Student_info>::size_type str_sz;
    typedef vector<Student_info>::iterator iter;
    iter it = students.begin();
    str_sz i = 0, count = 0;

    while (it != students.end()) {
        // fgrade tests wether or not the student failed
        if (!fgrade(*it)) {         
            // if student passed, copy to front of vector
            students.insert(students.begin(), it, it);      
            // tracks of the number of passing students(so we can properly resize the array)
            count++;                                        
        }

        cout << it->name << endl;   // output to verify that each student is iterated to
        it++;
    }
}

Код компилируется и запускается, но вектор students не добавляет к своему началу никаких структур student. Вывод моей программы показывает, что вектор students не изменился.

Вот мой полный исходный код, за которым следует образец входного файла (я перенаправляю ввод из консоли, введя «‹ Оценки» после имени скомпилированной программы в командной строке.)

#include <iostream>
#include <string>
#include <algorithm>    // to get the declaration of `sort'
#include <stdexcept>    // to get the declaration of `domain_error'
#include <vector>       // to get the declaration of `vector'

//driver program for grade partitioning examples

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::domain_error;
using std::sort;
using std::vector;
using std::max;
using std::istream;

struct Student_info {
    std::string name;
    double midterm, final;
    std::vector<double> homework;
};


bool compare(const Student_info&, const Student_info&);
std::istream& read(std::istream&, Student_info&);
std::istream& read_hw(std::istream&, std::vector<double>&);
double median(std::vector<double>);

double grade(double, double, double);
double grade(double, double, const std::vector<double>&);
double grade(const Student_info&);

bool fgrade(const Student_info&);

void extract_fails(vector<Student_info>& v);

int main()
{
        vector<Student_info> vs;
        Student_info s;
        string::size_type maxlen = 0;
        while (read(cin, s)) {
                maxlen = max(maxlen, s.name.size());
                vs.push_back(s);
        }

        sort(vs.begin(), vs.end(), compare);

    extract_fails(vs);


    // display the new, modified vector - it should be larger than
    // the input vector, due to some student structures being
    // added to the front of the vector.
    cout << "count: " << vs.size() << endl << endl;
    vector<Student_info>::iterator it = vs.begin();
    while (it != vs.end())
        cout << it++->name << endl;
    return 0;
}


// Extract the students who failed from the "students" vector.
void extract_fails(vector<Student_info>& students)
{
    typedef vector<Student_info>::size_type str_sz;
    typedef vector<Student_info>::iterator iter;
    iter it = students.begin();
    str_sz i = 0, count = 0;

    while (it != students.end()) {
        // fgrade tests wether or not the student failed
        if (!fgrade(*it)) {         
            // if student passed, copy to front of vector
            students.insert(students.begin(), it, it);      
            // tracks of the number of passing students(so we can properly resize the array)
            count++;                                        
        }

        cout << it->name << endl;   // output to verify that each student is iterated to
        it++;
    }
}


bool compare(const Student_info& x, const Student_info& y)
{
    return x.name < y.name;
}


istream& read(istream& is, Student_info& s)
{
    // read and store the student's name and midterm and final exam grades
    is >> s.name >> s.midterm >> s.final;

    read_hw(is, s.homework);  // read and store all the student's homework grades
    return is;
}


// read homework grades from an input stream into a `vector<double>'
istream& read_hw(istream& in, vector<double>& hw)
{
    if (in) {
        // get rid of previous contents
        hw.clear();

        // read homework grades
        double x;
        while (in >> x)
            hw.push_back(x);

        // clear the stream so that input will work for the next student
        in.clear();
    }
    return in;
}


// compute the median of a `vector<double>'
// note that calling this function copies the entire argument `vector'
double median(vector<double> vec)
{
    typedef vector<double>::size_type vec_sz;

    vec_sz size = vec.size();
    if (size == 0)
        throw domain_error("median of an empty vector");

    sort(vec.begin(), vec.end());

    vec_sz mid = size/2;

    return size % 2 == 0 ? (vec[mid] + vec[mid-1]) / 2 : vec[mid];
}


// compute a student's overall grade from midterm and final exam grades and homework grade
double grade(double midterm, double final, double homework)
{
    return 0.2 * midterm + 0.4 * final + 0.4 * homework;
}


// compute a student's overall grade from midterm and final exam grades
// and vector of homework grades.
// this function does not copy its argument, because `median' does so for us.
double grade(double midterm, double final, const vector<double>& hw)
{
    if (hw.size() == 0)
        throw domain_error("student has done no homework");
    return grade(midterm, final, median(hw));
}


double grade(const Student_info& s)
{
    return grade(s.midterm, s.final, s.homework);
}


// predicate to determine whether a student failed
bool fgrade(const Student_info& s)
{
    return grade(s) < 60;
}

Пример входного файла:

Му 100 100 100 100 100 100 100 100
Неудача1 45 55 65 80 90 70 65 60
Мур 75 85 77 59 0 85 75 89
Норман 57 78 73 66 78 70 88 89
Олсон 86 70 90 55 73 80 84
Пирсон 47 70 82 73 50 87 73 71
Бейкер 67 72 73 40 0 ​​78 55 70
Дэвис 77 70 82 65 70 77 83 81
Эдвардс 77 72 73 80 90 93 75 90
Неудовлетворительно2 55 55 65 50 55 60 65 60

Спасибо всем, кто найдет время, чтобы посмотреть на это!


person Darel    schedule 04.04.2010    source источник


Ответы (3)


Вставка в начало вектора делает недействительными все итераторы, указывающие на этот вектор. Если я не ошибаюсь, вы должны использовать <algorithm> - пару std::remove/std::erase.

person Nikolai Fetissov    schedule 04.04.2010
comment
Я не использовал ‹алгоритм›, но упоминание о том, что вставка в начало вектора делает недействительными все итераторы, было точным, я думаю... Я переписал функцию, используя индекс: while (i != student.size()) { if (!fgrade(students[i])) { student.insert(students.begin(), student[i++]); количество++; } я++; } студенты.изменить размер(количество); это работает сейчас! Спасибо. - person Darel; 05.04.2010

Стоит отметить, что другим людям, пытающимся решить эту проблему, здесь была еще одна проблема:

insert(students.begin(), it, it)

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

Другими словами, диапазон, обозначенный [it, it), является пустым диапазоном. Чтобы получить диапазон только «это», вам понадобится [it, it+1).

Конечно, это по-прежнему будет иметь проблему с аннулированием всех итераторов перед ним.

person Andy Isbell    schedule 21.09.2012

Я удивлен, что это сработало для тебя, Дарел. я пытался использовать

students.insert(students.begin(), students[i++]);

и мне бросили bad_alloc. Мне пришлось использовать этот формат:

students.insert(students.begin(), 1, students[i+count]);

чтобы заставить его работать. Добавленный 1 – это количество элементов для вставки, инициализированное значением students[i+count].

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

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

person James    schedule 24.02.2011
comment
У меня все еще есть мой код для этого дома, я посмотрю на него сегодня вечером и посмотрю, не упустил ли я какой-то критический нюанс своего решения. - person Darel; 25.02.2011