Професионална програма
Loading...
+ Нов въпрос
AndonAndonov avatar AndonAndonov 1 Точки

Проблем със задача 8 от C++ Programming - февруари 2017

8. Write a function 
int * parseNumbers(const string& str, int& resultLength) which returns a pointer to new-allocated array with the numbers parsed from str (assume you don’t need to handle wrongly-formatted input). str will contain integer numbers separated by spaces. The function writes the length of the allocated array in resultLength. Write a program which lets the user enter a number of lines of integers from the console, and prints their sum. Use the parseNumbers function in your program, but make sure you delete each array once you’re done with it.
Example input (note: first line is the count of lines of numbers, in this case: 2 lines):
2
1 2 3
4 5
Expected output (sum of 1 2 3 and 4 5): 15


Въпросът ми е как едновременно да създам stringstream oт потребителския стринг с данни str във функцията int * parseNumbers(const string& str, int& resultLength) и като викам същата функция да й подам параметър за размера на stringstream, който се намира в нея? blush

int * parseNumbers(const string& str, int& resultLength)
{
    int *userInputNums = new int[resultLength];
    stringstream userInputStream(str);
    
    int size = 0;
    
    while(1)
    {
        int i;
        userInputStream >> i;
        if (!userInputStream)
        {break;}
        ++size;
    }
    
    for (int i = 0; i < resultLength; i++)
    {
        userInputStream >> userInputNums[i];
    }
    return userInputNums;
}

Тагове:
0
C++ Programming 12/03/2017 18:59:12
MartinBG avatar MartinBG 3828 Точки
Best Answer

Аз разбирам условието така:

parseNumbers приема като параметър const string& str, който съдържа неизвестен към момента брой числа от тип int.

Във функцията трябва да се определи броя на числата в стринга (аз използвам друга функция за целта, която прави само това), който се запазва (и връща по-късно) чрез int& resultLength. След като е извесетен броя елементи, се създава нов масив (int * pArr = new int[resultLength];) и се попълва с числата от const string& str. Накрая се връща указател към масива (int * parseNumbers(...)), заедно с броя елементи в него (int& resultLength).

Масива трябва да се изтрие по-късно в main():

int* pArr = parseNumbers(str, resultLength);

...

delete [] pArr;

1
12/03/2017 14:02:10
georgi.stef.georgiev avatar georgi.stef.georgiev 921 Точки

Идеята на @MartinBg е вярната (можете да му маркирате отговорa му за верен и да го upvote-нете). Ето малко разяснения от мен.

Стандартния подход за "връщане" на масив от функция (без да се ползват vector или други stl контейнери, за които все още не сме учили) е да върнеш pointer към динамично заделен масив (заделен с new тоест) - но понеже трябва да знаеш и размера на този масив, за да го използваш от викащия код, отделно в една референция записваш колко се получава да е дълъг. Пример за код, който вика тази функция и принтира елементите на масива ред по ред би изглеждал така:

string input;

cin >> input;

int parsedLength; //we expect parseNumbers to set the value of this

int * parsed = parseNumbers(input, parsedLength)

for (int i = 0; i < parsedLength; i++) {

    cout << parsed[i] << endl;

delete[] parsed;

}

Един прост начин да знаете колко памет да заделите в parseNumbers е два пъти подред да направите stringstream по input-а - както правихме в едно от демата за stringstream (това с HTML output-а) - първия път само броите колко числа сте прочели, след това заделяте масив с тази големина, след това минавате пак по input-а със stringstream и тогава вече записвате числата по съответните позиции в масива. Това не е най-ефикасния метод, но е приемлив за тази задача.

Най-добрия вариант е да преброите числата на базата на space-овете пред тях (обърнете внимание, че в задачата изрично пише, че числата са разделени със space, тоест всеки два поредни символа, първия от които е space, а за втория isdigit връща true, e число (не забравяйте да преброите и първото число). Не е достатъчно да преброите само space-овете, защото задачата не гарантира, че числата са разделени само с по един space (тоест това е валиден вход: 1 3    7 13).

Има и трети, "по-напреднал" вариант, който ако се замислите добре можете да го постигнете със знанията дотук, ако на някой му се занимава - проучете как горе-долу работи vector в C++, или List в C#, или ArrayList в Java (един и същ принцип стои зад тях).

Поздрави,

Жоро

 

Edit: демо 19 от лекцията (за което нямахме време на самата лекция) ползва същата концепция - само че то връща string* към динамично заделен масив. И там заделянето на памет нарочно е направено с лош performance, за да е ясно как може да се заделят и освобождават в една функция много масиви (има коментар с подробности там) - подобен на този код, само че за int, би ви вършил работа, но ви препоръчвам да ползвате някое от горните предложения.

0
12/03/2017 15:22:58
Dimitar_Petkov_Petkov avatar Dimitar_Petkov_Petkov 169 Точки

А премливо ли е, след като прочетем първото число и знаем колко реда да очакваме да си създадем (динамично) масив от толкова на брой указатели, всеки от които да сочи към отделен масив (пак динамично заделен). И чрез един loop да си заредим с getline всеки от масивите. Зная ,че това е по-скоро C style подход но със сегашните познания е работещ вариант, поне за мен.

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

Да, но бих казал, че в случая е ненужно да държиш всичката памет заета до края на програмата - предвид, че само ще сумираш елементи, е достатъчно да четеш по един масив, да го обработваш (сумираш) и след това да освобождаваш паметта. Този масив няма да ти трябва след като го сумираш веднъж, така че няма смисъл да стои в паметта след това. Разбира се, пак трябва всеки един getline ред да го обработиш в тази parseNumbers функция, за да получиш pointer-а, който искаш да запазиш в масива от pointer-и (иначе няма как да знаеш предварително колко точно числа има на този ред и няма как да заделиш предварително за самите числа тази памет - но можеш да я заделиш за редовете, тоест за броя масиви, както ти предлагаш)

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

0
ThePSXHive avatar ThePSXHive 436 Точки

Според описанието се получава "Параграф-22"; трябва да заделиш памет за определен брой елементи, който не е предварително известен, но от друга страна трябва да знаеш броят на елементите, за да можеш въобще да заделиш памет за тях. Ако броят е предварително известен (и не е необходимо стойността му да бъде установена в parseNumbers() функцията), то тогава не е ясно защо се предава по референция. Другият вариант е да напишеш отделна функция в която да преброиш броят на разстоянията (като тук презумпцията е, че между всяка стойност разстоянието е единично), и след това да добавиш единица. Ако броят на разстоянита е m, то тогава стойността на resultLength ще бъде m + 1. На всеки от n-те на брой реда на които се въвеждат стойности се извиква тази отделна функция, резултатът се запазва в resultLength и след това заедно с въведения стринг се подават на parseNumbers(). Но отново не е ясно защо размерът за алокирането е предаван по референция. Иначе, не инициализираш size, и съответно не се знае каква стойност съдържа след декларирането на променливата. Освен това не delete[]-ваш след като си new[]-нал.

0