Построение интеграции Эйлера с использованием Polyline(), C++

Итак, я пытаюсь построить вывод этой функции интеграции Эйлера:

typedef double F(double,double);
using std::vector;

void euler(F f, double y0, double a, double b, double h,vector<POINT> Points)
{

POINT Pt;
double y_n = y0;
double t = a;
for (double t = a; t != b; t += h )

{
    y_n += h * f(t, y_n); 

    Pt.x = t; // assign the x value of the point to t.
    Pt.y = y_n; // assign the y value of the point to y_n.
    Points.push_back(Pt);

}


}


// Example: Newton's cooling law
double newtonCoolingLaw(double, double t) 
{
    return t; // return statement ends the function; here, it gives the time derivative y' = -0.07 * (t - 20)
}

Я пытаюсь использовать функцию Polyline() в приложении Win32, поэтому я делаю это в случае WM_PAINT:

case WM_PAINT:
    {

    hdc = BeginPaint(hWnd, &ps);

  //Draw lines to screen.

    hPen = CreatePen(PS_SOLID, 1, RGB(255, 25, 5));
    SelectObject(hdc, hPen);


    using std::vector;
    vector<POINT> Points(0);

    euler(newtonCoolingLaw, 1, 0, 20, 1,Points);

    POINT tmp = Points.at(0);
    const POINT* elementPoints[1] = { &tmp };

    int numberpoints = (int) Points.size() - 1 ;


    Polyline(hdc,elementPoints[1],numberpoints);

Когда я перенаправляю свой ввод-вывод на консоль, вот выходные данные для переменных:

введите здесь описание изображения

Я могу нарисовать ожидаемые линии на экране с помощью MovetoEx(hdc,0,0,NULL) и LineTo(hdc,20,20), но по какой-то причине ни одна из этих функций не работает с моим vector<POINT> Points. Какие-либо предложения?


person Robbie Capps    schedule 19.02.2015    source источник


Ответы (1)


Есть несколько вещей, которые кажутся мне ошибочными:

1) Вы должны передать вектор по ссылке или как возвращаемое значение:

void euler(/*...*/,vector<POINT>& Points)

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

2) Не сравнивайте двойные числа на (не)равенство в заголовке цикла for. Двойники имеют ограниченную точность, поэтому, если b намного больше, чем h, ваш цикл может никогда не завершиться, так как t никогда не может точно соответствовать b. Вместо этого сравните «меньше»:

for (double t = a; t < b; t += h )

3) Почему вы объявляете elementPoints как массив указателей размера 1? Не будет ли простой указатель:

const POINT* elementPoints =  &tmp ; //EDIT: see point 5)

4) У вас есть ошибка по одному при вызове Polyline. Если вы хотите вообще придерживаться массива, используйте.

Polyline(hdc,elementPoints[0],numberpoints);

РЕДАКТИРОВАТЬ: Извините, я забыл важное:

5) В вашем коде elementPoints[0] указывает на один double (tmp), а не на массив внутри вектора. Это, вероятно, сработает, если вы объявите tmp как ссылку:

POINT& tmp = Points.at(0); //I'm wondering why this doesn't throw an exception, as the vector should actually be empty here

Однако я думаю, что вы на самом деле хотите избавиться от tmp и elementPoints вообще и написать в последней строке:

Polyline(hdc,&Points[0],(int) Points.size()-1);
//Or probably rather:
Polyline(hdc,&Points[0],(int) Points.size());

Кстати: Какова цель -1?

person MikeMB    schedule 19.02.2015
comment
Большое спасибо! Я полный новичок, и вы только что научили меня многому. Я работаю над этой одной проблемой некоторое время. - person Robbie Capps; 19.02.2015
comment
Кроме того, чтобы ответить на некоторые из ваших вопросов о моем ужасном коде: tmp, как он был определен, не вызывал исключения, потому что я обычно определял произвольный размер вектора Points, чтобы он этого не делал. -1 был моим недостатком, так как я неправильно истолковывал проблемы, которые у меня были раньше, которые, как вы указали, вероятно, были связаны с неправильным использованием elementPoints. Просто для уточнения, &Points[0] является ссылкой на элемент 0 в Points? - person Robbie Capps; 19.02.2015
comment
@Robbie: Не беспокойтесь, комбинация указателей, ссылок, массивов и векторов в нескольких строках кода поначалу может показаться довольно запутанной. В этом контексте & означает получение адреса, поэтому &Points[0] в основном возвращает адрес первого элемента (элемент 0) в векторе. Поскольку векторы хранят свои элементы внутри в виде массива, это эквивалентно начальному адресу внутреннего массива POINTs, что и ожидает Polyline. Однако я должен признать, что я не уверен, что это абсолютно гарантировано стандартом. - person MikeMB; 19.02.2015
comment
@Robbie: я также исправил синтаксическую ошибку в своем предложении для tmp. & нужно ставить после типа и перед именем переменной (так же, как * для указателей). - person MikeMB; 19.02.2015
comment
Ну, это, безусловно, дает результаты, которые я хотел, по крайней мере, для демонстрации того, что это может работать. Спасибо еще раз. Вы действительно помогли мне глубже понять некоторые из этих вещей. - person Robbie Capps; 19.02.2015
comment
Последний блок кода является важным. Но избавьтесь от приведения (int). - person Ben Voigt; 19.02.2015
comment
@Бен Фойгт: Почему? Интерфейс ожидает int, size() возвращает size_t. Я склонен делать такие приведения явными (и затем посмотреть, как я могу избежать их, изменив интерфейс, если это возможно). Это плохая практика? - person MikeMB; 19.02.2015
comment
Вычеркните комментарий в скобках. Обычно я пытаюсь изменить тип аргумента, но в этом случае я бы предпочел, чтобы интерфейс принимал целое число без знака, что кажется мне более естественным. - person MikeMB; 19.02.2015
comment
@MikeMB: Нехорошо использовать приведения типов там, где они не нужны, потому что они скрывают ошибки. Приведения в стиле C особенно плохи. Например, см. rextester.com/UQAR98460. - person Ben Voigt; 19.02.2015
comment
@Ben Voigt: Хотя я согласен с тем, что следует избегать приведения типов, где это возможно (сделав их ненужными), явное приведение, на мой взгляд, лучше, чем неявное. Вы не можете избежать приведения здесь, потому что параметры двух библиотечных функций различны. Без явного приведения компилятор выдаст предупреждение, но вы ничего не сможете с этим поделать. Сделайте это с большим проектом, и вы будете завалены предупреждениями и пропустите новые (важные). - person MikeMB; 19.02.2015
comment
@Ben: В общем, я также предпочитаю приведения типов в стиле С++, но, с другой стороны, они часто делают код менее читаемым. Вот почему я использую их для преобразования встроенных типов внутри списка параметров, но это, вероятно, вопрос вкуса или руководства по кодированию, которого вы хотите/должны придерживаться. - person MikeMB; 19.02.2015
comment
@MikeMB: неявного приведения не существует. Существуют неявные преобразования, и они более безопасны, чем приведения типов. В тех случаях, когда неявное преобразование может привести к потере данных (и предупреждению компиляции), лучше использовать встроенную вспомогательную функцию, которая обертывает неявное преобразование (и также реализовать THAT без приведения, чтобы предотвратить сокрытие серьезных ошибок типов). Таким образом, предупреждение возникает только во вспомогательной функции и может выборочно игнорироваться с помощью прагм. Например. Polyline(hdc,&Points[0],narrowing<int>(Points.size())); может быть предпочтительнее Polyline(hdc,&Points[0], Points.size()); - person Ben Voigt; 19.02.2015
comment
и неявное преобразование в Polyline(hdc,&Points[0],Points.size()); должно быть предпочтительнее любого варианта с приведением. - person Ben Voigt; 19.02.2015
comment
@Ben: Да, я использовал здесь неправильное выражение. Однако все, что я прочитал до сих пор, указывает на то, что обе версии Polyline(hdc,&Points[0],Points.size()); и Polyline(hdc,&Points[0],(int)Points.size()); будут создавать один и тот же код. Если это неправильное предположение с моей стороны, не могли бы вы направить меня к дополнительным материалам для чтения по этой теме, потому что я действительно хотел бы понять, что происходит под капотом. - person MikeMB; 20.02.2015
comment
@MikeMB: все варианты, которые мы обсуждаем, будут генерировать один и тот же код. Однако версия с явным преобразованием может не генерировать диагностику даже при наличии серьезной проблемы (ложноотрицательный результат), а версия с неявным преобразованием может генерировать диагностику шума (ложноположительный результат). На самом деле эти последние диагностики хороши, потому что они привлекают внимание программиста, но, как вы говорите, остаются отвлечением после того, как программист проанализировал сужающее преобразование. Что нужно, так это синтаксис, чтобы заглушить предупреждение о сужении, но при этом диагностировать серьезные ошибки типа, которые возникают. - person Ben Voigt; 20.02.2015
comment
Такой синтаксис может быть предоставлен с нулевой стоимостью выполнения через template <typename To, typename From> inline To narrowing(const From& val) { return val; } (можно подумать о включении идеальной переадресации, но действительно ли это стоит, когда основное использование происходит с примитивными типами?) Теперь size_t f(); void g(int); g(narrowing<int>(f()))); не будет выдавать предупреждений, а size_t f(); void g(int); g(narrowing<int>(f)); по-прежнему будет диагностировать ошибку, т.к. делает size_t* f(); void g(int); g(narrowing<int>(f())); - person Ben Voigt; 20.02.2015