Софтуерно Инженерство
Loading...
zzerro avatar zzerro 11 Точки

Интерполация (от упражненията)

Здравейте!

В упражненията беше даден този цикъл, който извършва същинската интерполция:

    double step = dataSize / (double) numSamples;

    int posInResult = 0;

    for (double pos = 0; pos < dataSize; pos += step) {
        int actualPos = (int) pos;

        result [posInResult] = data [actualPos];
        posInResult++;
    }

Също така ставаше дума и за добра четимост. Искам да предложа още един вариант със съвсем познат вид на цикъла:

    double step = dataSize / (double) numSamples;

    double posInResult = 0;

    for (int i = 0; i < numSamples; i++) {
        result [i] = (int) posInResult;
        posInResult = posInResult + step;
    }

0
C++ Programming 15/03/2017 12:32:05
georgi.stef.georgiev avatar georgi.stef.georgiev 916 Точки

Искаш да кажеш:

double step = dataSize / (double) numSamples;

double posInResult = 0; double posInData = 0;

for (int i = 0; i < numSamples; i++) {
    result [i] = (int) posInResult; result[i] = data[(int)posInData];
    posInResult = posInResult + step; posInData = posInData + step;
}

предполагам ;) Ние на ръка тествахме с числа, които съвпадаха с индексите, но ако са други числата, твоето няма да работи. Но предполагам е просто недоглеждане.

Иначе да, това също е добър вариант на кода за тази задача :)

Поздрави,

Жоро

0
zzerro avatar zzerro 11 Точки

Благодаря за бързия отговор!

Все пак въпросът е какво точно искаме да получим чрез интерполацията: индексите в testData[] (моят вариант) или направо стойностите (лекцията). Всичко си работи, но изходът трябва да се оправи:

    for(int i = 0; i < 10; i++) {
        cout << samples[i] << endl;     cout << testData [ samples[i] ] << endl;
    }

Поздрави и на теб!

0
georgi.stef.georgiev avatar georgi.stef.georgiev 916 Точки

А, да, сега разбирам, да - ако го ползваш така, ще работи както трябва. Още една причина, поради която трябваше да кръстя по време на упражнението резултата на функцията sampledData (и тогава ти щеше да кръстиш твоя вариант sampledIndices, или нещо такова), за да е по-ясно

0
zzerro avatar zzerro 11 Точки

result [i] = (int) posInResult; result [i] = data [ (int) posInData ];

ето това май е най-доброто за копиране на стойности...

Ама аз имам друг въпрос. По време на упражненията до мен седеше едно момче от Сърбия и ме попита защо функцията е със звезда: int *getSamples(int data[], int dataSize, int numSamples).... Тръгнах да му обяснявам, че не можем да върнем пойнтер, ако функцията е от друг вид, т.е. всяка функция връща точно и само типа, в който е декларирана. Обаче се запънах, защото в мен се породи съмнение за въпросния нов масив, който създаваме в нея. Мислех си няма ли да стане същото, ако го направим като обикновен масив и запазим само пойнтера към него? Бях му обещал да направя тема във форума - и ето я laugh Досега се мъчих, обаче ако направим масив по обикновения начин, то в него не остават нито индекси, нито стойности пренесени от testData []. Ако някой иска да пробва (или има друга идея - нека я предложи), ето какво опитвах:


    int* result = new int[numSamples]; // работещ вариант

    int indexData[numSamples]; //неработещ
    int* result = indexData;        //вариант. След излизане от функцията всичко се губи.

0
georgi.stef.georgiev avatar georgi.stef.georgiev 916 Точки

Хубав въпрос. Когато създаваш от "нормалните" масиви, или "статични" масиви както понякога се наричат, тези масиви се пазят в програмния stack, същото място където се пазят нормални променливи от примитивните типове (int, double и т.н.). Както говорихме на първата лекция, всички такива променливи "живеят" само в блока, в който са декларирани. Тоест, в момента в който функцията приключи, паметта за тези променливи - в случая за твоя масив - се освобождава за използване от другаде.

Това означава, че ако върнеш pointer към някой от тези адреси в паметта, ти връщаш pointer към нещо, което вече не е на твое разположение за ползване. Какво програмата е направила с това нещо вече е недефинирано поведение - може да го е занулила, може да е презаписала нещо друго, може и някоя друга програма да го е взела и да си го ползва (това последното не би трябвало да се случи със stack памет, но недефинираното поведение си е недефинирано поведение и не можеш да си сигурен). Затова в лекцията за масиви, за тези "статични" масиви казахме, че не можеш да ги "връщаш" от функция - това включва и да направиш pointer към тях и да го върнеш него.

Същият проблем би имал ако върнеш pointer или reference към каквато и да е нормална променлива декларирана във функция, независимо дали е масив, int, double или нещо друго, което е инициализирано БЕЗ new. Нещата инициализирани с new обаче, както учихме, не се освобождават автоматично от паметта и затова може да си ги размятаме напред-назад между функции, докато не кажем изрично delete, което ще ги освободи. Между другото, името за паметта, която е инициализирана БЕЗ new (тази на която викам stack памет) е auto памет, защото автоматично се заделя и освобождава, за разлика от тази заделена с new, която ръчно трябва да управляваш.

0
zzerro avatar zzerro 11 Точки

Тогава защо се обърках? Защо това работи без new:

#include<iostream>
#include <string.h>
using namespace std;

void changeAll (char newName[], int* newInt)
{
    strcpy (newName, "opla");
    *newInt = 10;
}

int main ()
{
    char oldName [5] = "miss";
    int oldInt = 5;

    cout << "Old values: " << oldName << " " << oldInt << endl;
    changeAll(oldName, &oldInt);
    cout << "Old varibales again?? :  " << oldName << " " << oldInt << endl;

    return 0;
}

0
georgi.stef.georgiev avatar georgi.stef.georgiev 916 Точки

Помисли за това, което ти казах за живота на една променлива. В примера, който даваш, oldName е деклариран в main. Оттам нататък ти го подаваш на changeAll - това е ок, защото масив като параметър не се копира. Тоест там не се създава вътрешна за changeAll функцията променлива на име newName. Това е защото, както говорихме, масивите параметри се предават по референция, не по копие. Тоест changeAll не контролира живота на newName, а просто работи върху някакъв масив, който нарича newName, но това всъщност е самия oldName масив, чиито живот се контролира от main в случая. И тъй като main не е приключил, oldName си е валиден масив, когато го ползваш в main.

Ако вече беше направил като локална променлива вътре в changeAll, която е някакъв масив, този локален масив няма как да го дадеш на main, защото като излезе changeAll, той ще бъде освободен. Пробвай ако искаш да го направиш това в твоя пример - вместо да работиш върху newName, копирай му елементите един по един в локален char масив и се опитай да върнеш pointer към този локалния.

Затова не можем да връщаме масив от функция, но пък можем да приемем като параметър масив, върху който да работим (или с този синтаксис, който ти си дал, или като приемем pointer към масива), защото параметъра масив всъщност не е собственост на функцията, а е собственост на викащия, а функцията го взема на заем за малко за да направи нещо по него :)

1
IvanMitkov avatar IvanMitkov 19 Точки

Имаш едно място в паметта, това ти е масива в мейн. В това място има нещо което си записал. След това даваш същото това място на друга функция и и казваш запиши други неща върху това място. Но мейн знае това място в паметта. И когато другата функция приключи мейн просто отива на мястото което знае и взима това което има в него.

Когато създаваш нещо в хийпа е същата работа. Ти си запазваш едно място в паметта - което не изчезва с функцията , както е в ако е само в стека и можеш да пратиш това място където си искаш и да кажеш на друга функция прочети каквото има на това място.

Ако обаче правиш нещо само в стека т.е. без new това нещо съществува само до следващите къдрави скоби. Като минат скобите и програмата го изтрива - взима си паметта която е била алокирана за стека. И ако кажеш на друга функция отиди на това място и вземи каквото има там, тя няма да намери нищо, или поне не това което очакваш, защото вече друга програма може да е написала нещо отгоре.

Програмите пазят паметта в хийпа, но не и тази в стека.

0
zzerro avatar zzerro 11 Точки

...И тъй като main не е приключил, oldName си е валиден масив, когато го ползваш в main...

... или в която и да е извикана функция.

А, ето кое ми се губеше. Въпросът не е само в мястото (стек или хийп), а по-скоро във времето. Излиза, че виканият (съществуващ по-малко време) може да работи трайно върху променливите на викащия. А, колкото за new - каквото направим с него, можем да го ползваме през цялото време и да го виждаме от всички функции...

А, какво за глобалните променливи или масиви?

0
15/03/2017 20:08:06
zzerro avatar zzerro 11 Точки

...без new това нещо съществува само до следващите къдрави скоби...

"следващите къдрави скоби" - но разклоненията могат да връщат към корена. Разбрах, мерси!

0
georgi.stef.georgiev avatar georgi.stef.georgiev 916 Точки

Да, и аз мисля, че се ориентира вече, спрямо това, което пишеш.

Глобалните са все едно в най-големите (най-"root") скоби, т.е. живеят през цялото време, през което програмата живее. На практика все едно са в main (защото програмата живее докато main живее), само че синтактично ги виждаш и от други функции

0
zzerro avatar zzerro 11 Точки

Благодаря за отговорите!

Ще покажа темата и на момчето, което питаше...

0