Sadržaj
1 Алгоритми
Појам алгоритма
Запис алгоритма
Запис алгоритма - квиз
Разумевање алгоритама датих дијаграмом - квиз
Разумевање алгоритама датих псеудокодом - квиз
Програмирање слагањем блокова
2 Како се програмира
Изглед програма
О језичким правилима
Језичка правила - квиз
Интегрисано окружење за развој програма
Интегрисано окружење - квиз
Кориснички интерфејс програма
Кориснички интерфејс - квиз
3 Променљиве, подаци, типови
Променљиве, подаци, типови
3.1 Реални бројеви
Реални бројеви
Реални бројеви - квиз
Реални бројеви - задаци
3.2 Целобројни типови
Целобројни типови
Целобројни типови - квиз
Целобројни типови - задаци
3.3 Конверзије и заокруживање
Конверзије нумеричких типова и заокруживање
Конверзије и заокруживање - квиз
Конверзије и заокруживање - задаци
3.4 Знаковни подаци
Знаковни подаци
Знаковни подаци - квиз
3.5 Текстуални подаци (стрингови, ниске)
Текстуални подаци (стрингови, ниске)
Стрингови - квиз
3.6 Логички подаци
Логички подаци
Логички подаци - квиз
4 Гранања
Гранања
4.1 Наредба if
Наредба if
Наредба if - квиз
Наредба if - задаци
4.2 Остали облици if наредбе
Остали облици if наредбе
Остали облици if наредбе - задаци
4.3 Сложени услови
Сложени услови
Сложени услови - квиз
Сложени услови - задаци
4.4 Неке типичне употребе if наредби
Налажење минимума и максимума
Налажење минимума и максимума - задаци
Пребројавање, издвајање и претрага
Пребројавање и издвајање - задаци
4.5 Угнежђено гранање
Угнежђено гранање
Угнежђено гранање - квиз
Угнежђено гранање - задаци
4.6 Наредба switch
Наредба switch
Наредба switch - задаци
4.7 Гранања - разни задаци
Гранања - разни задаци
5 Петље
Петље
5.1 Врсте петљи
Врсте петљи
5.2 Наредбе break и continue
Наредбе break и continue
5.3 Основни алоритми са петљама
Основни алоритми са петљама
Агрегатне функције
Агрегатне функције - задаци
Пресликавања
Пресликавања - задаци
Пребројавање и издвајање
Пребројавање и издвајање - задаци
Претраживање
Претраживање - задаци
Сложене операције
Сложене операције - задаци
5.4 Цифре броја
Цифре броја
Цифре броја - задаци
5.5 Дељивост
Дељивост
Прости бројеви
Прости бројеви - задаци
Еуклидов алгоритам
Еуклидов алгоритам - задаци
5.6 Угнежђене петље
Угнежђене петље
Угнежђене петље - квиз
Угнежђене петље - ASCII графика
Угнежђене петље - ASCII графика - квиз
Угнежђене петље - ASCII графика - задаци
Угнежђене петље - сегменти
Угнежђене петље - сегменти - квиз
Угнежђене петље - сегменти - задаци
5.7 Петље - разни задаци
Петље
Петље - разни задаци
6 Статички методи
Статички методи
6.1 Писање статичких метода
Писање статичких метода
Писање статичких метода - квиз
6.2 Извршавање метода
Извршавање метода
Извршавање метода - квиз
6.3 Пренос аргумената по референци
Пренос аргумената по референци
Пренос аргумената по референци - квиз
6.4 Корист од метода
Корист од метода
Методи - задаци
7 Низови
Низови
7.1 Употреба низова
Употреба низова
Употреба низова - квиз
7.2 Низови - вежбање
Низови - вежбање
Низови - задаци
7.3 Низ као референцирани тип
Низ као референцирани тип
Низ као референцирани тип - квиз
7.4 Листе
Листе
Листе - квиз
Операције над листама
Операције над листама - квиз
Листе и ефикасност
Листе и ефикасност - квиз
7.5 Стринг и низ карактера
Стринг и низ карактера
Стринг и низ карактера - квиз
Трансформисање стрингова
Трансформисање стрингова - квиз
8 Матрице
Матрице
8.1 Употреба матрица
Употреба матрица
Употреба матрица - квиз
Матрице - задаци
9 Кориснички дефинисани типови
Кориснички дефинисани типови
9.1 Набројиви типови и структуре
Набројиви типови
Структуре
Набројиви типови и структуре - квиз
10 Фајлови
Фајлови
10.1 Управљање фајловима
Управљање фајловима
Управљање фајловима - квиз
10.2 Читање и упис података у текстуални фајл
Читање и упис података у текстуални фајл
Текстуални фајлови - квиз
Текстуални фајлови - вежбање
10.3 Аргументи командне линије програма
Аргументи командне линије програма
Аргументи командне линије програма - квиз

Трансформисање стрингова

Под трансформисањем стринга подразумевамо било какав поступак који, полазећи од једног стринга, формира други. Уобичајени примери трансформација стринга су уклањање размака (и других белина) са почетка и краја стринга или претварање свих малих слова у велика. Ове и сличне трансформације су врло често потребне у реалним применама, па за њих постоје и готови методи. У таквом случају је најбоље користити те готове методе, како због њихове ефикасности тако и због јасноће програма.

За мање уобичајене трансформације стринга, које су специфичне за дати проблем, увек постоји више начина да извршимо потребну трансфомацију.

Један начин, који нам је скоро увек на располагању, је да поновљеним сабирањем (надовезивањем) стрингова израчунамо резултат. Нека је, на пример, потребно обрнути дати стринг, то јест формирати нови стринг који се састоји од истих карактера као дати, али у обрнутом редоследу. У приручнику је на страни 193 наведен метод Obrni, који обавља ову трансформацију:

static string Obrni(string s) {
    string t = "";
    foreach(char c in s) { t = c + t; }
    return t;
}

Овакви поступци се лако смишљају, углавном су једноставни за писање и јасни онима који читају код. За мале стрингове ови поступци су прихватљиви, али због вишеструког додељивања вредности стрингу t, могу да постану врло спори за велике стрингове. Као што смо научили, додељивање нове, макар и врло сличне вредности стрингу, подразумева формирање и попуњавање новог стринга, а то ову операцију чини знатно споријом него што изгледа.

У овој лекцији ћемо поменути неке уобичајене технике трансформисања стрингова, у којима се избегава сувише често додељивање вредности стринговима.

На крају лекције ћемо на конкретном примеру испитати утицај вишеструког додељивања вредности стрингу на перформансе поступка.

Конверзија у низ и из низа

Када се током трансформисања стринга не мења његова дужина већ само неки или сви елементи, конверзија у низ може бити добар начин да се избегну многобројна преписивања података. Поступак трансформисања датог стринга s помоћу низа у општем случају тече овако:

char[] a = s.ToArray();

// obavimo potrebne izmene elemenata niza

s = new string(a);

Пошто конверзија подразумева копирање елемената колекције, у случају дугачких низова и стрингова ни конверзије не треба често изводити. У нашем примеру имамо само конверзију стринга у низ на почетку поступка и обрнуту конверзију на крају.

Такође треба водити рачуна о томе да за низ карактера a, метод a.ToString() враћа резултат "System.Char[]", што је текстуални опис овог објекта, а не резултат конверзије објекта у стринг. Ако као вредност неког стринга добијете "System.Char[]", вероватно сте омашком уместо наредбе облика new string(a) написали a.ToString().

Пример - формирање обрнутог стринга помоћу низа

У Приручнику је на страни 201 наведен метод Obrni, који за дати стринг s формира нови стринг са истим словима у обрнутом редоследу:

static string Obrni(string s) {
    char[] t = s.ToCharArray();
    int n = t.Length;
    for(int i = 0; i < n/2; i++) {
        char c = t[i];
        t[i] = t[n - 1 - i];
        t[n - 1 - i] = c;
    }
    return new string(t);
}

На сличан начин се од стринга може формирати и листа. Ово може да буде корисно ако је трансформација стринга таква да не знамо унапред дужину трансформисаног стринга.

List<char> a = new List<char>(s);

По обављеној трансформацији, обрнути процес прављења стринга на основу листе не може да се изведе директно, али може из два корака (што је довољно добро). Потребно је прво конвертовати листу у низ, а затим тај низ проследити као аргумент конструктору стринга:

string s = new string(a.ToArray());

Грађење стринга

Када неки стринг формирамо тако што почнемо од празног стринга који онда постепено допуњавамо, бољи начин да формирамо тај стринг је употреба објекта који се зове StringBuilder (градитељ стринга). Типична употреба градитеља стринга изгледа овако:

using System.Text; // imenski prostor u kome se nalazi StringBuilder
// ...

StringBuilder sb = new StringBuilder();

// ...
sb.Append(s); // pozivamo vise puta za razne stringove s

string rezultat = sb.ToString();

Однос између стринга и градитеља стринга је сличан као однос између низа и листе. Помоћу метода Append градитеља стринга се стринг који градимо продужава на врло сличан начин као што се листа продужава методом Add. Захваљујући томе се стринг може формирати са много мање реалоцирања (и преписивања) и зато је овакав приступ много ефикаснији од сличног, али наивног:

string rezultat = "";

// ...
rezultat += s; // pozivamo vise puta za razne stringove s

Пример - формирање обрнутог стринга помоћу градитеља стринга

Решимо задатак обртања стринга и употребом градитеља стринга. Потребно је само сваки карактер стринга s почев од краја ка почетку додати у градитељ стринга.

static string Obrni(string s)
{
    StringBuilder sb = new StringBuilder();
    for (int i = s.Length - 1; i >= 0; i--)
        sb.Append(s[i]);
    return sb.ToString();
}

Комбиновање метода из библиотеке

У неким случајевима, иако не постоји готов метод који би у једном кораку обавио потребну трансформацију, употребом више метода (или једног метода више пута) можемо да добијемо резултат који желимо.

Пример - формирање обрнутог стринга комбиновањем метода

Прегледањем списка метода класе string видимо да не постоји метод ове класе који креира обрнути стринг. Уз мало додатног трагања у именском простору System.Linq можемо наћи генерички поступак Reverse, који обрће било какву секвенцу. Резултат рада овог метода је секвенца, која се не може директно конвертовати у стринг, али може у низ карактера, који затим користимо за формирање стринга. Тако до циља стижемо у три корака:

  • позивом метода Reverse добијамо секвенцу у обрнутом редоследу

  • позивом метода ToArray конвертујемо ту секвенцу у низ карактера

  • позивом конструктора стринга са низом као аргументом, формирамо тражени стринг

static string Obrni(string s)
{
    return new string(s.Reverse().ToArray());
}

Колико споро је споро

Подсетимо се искуства са листама и убацивања и избацивања појединачних елемената у, односно из листе. Видели смо да свака таква операција изазива велики број премештања других елемената, па при многоструком понављању таквих операција време рада поступка порасте до неприхватљиво спорог.

На основу тог искуства можемо очекивати да и вишеструке измене дугачког стринга могу довести до врло спорих програма. У наставку ћемо спровести експеримент попут оног са листама, у коме ћемо упоредити по перформансама различите начине обављања једне трансформације датог стринга.

Нека је потребно у датом стрингу сва слова и цифре заменити доњом цртом.

Програм који следи извршава ову трансформацију на 4 начина:

  1. помоћу градитеља стринга;

  2. конверзијом стринга у низ и обрнуто;

  3. вишеструком применом метода Replace;

  4. формирањем резултујућег стринга простим надовезивањем карактера (без употребе градитеља стринга);

У програму се мере времена рада ових поступака за стринг који се састоји од \(N\) = 10000 пута поновљене секвенце, састављене од свих енглеских слова и цифара (укупно \(N \cdot (26+26+10) = 10000 \cdot 62 = 620~000\) карактера).

Добијени су следећи резултати (ако будете извршавали овај програм, извесно ћете добити другачија времена, али ће међусобни односи тих времена бити приближно овакви):

StringBuilder:                      00:00:00.0028678
Konverzija u niz i iz niza:         00:00:00.0087117
Primena Replace slovo po slovo:     00:00:00.0328468
Nadovezivanje karaktera na string:  00:00:46.3787325

Дакле, време рада сваког од прва три поступка се изражава деловима секунде, док последњни поступак траје нешто више од 46 секунди. Видимо да је сваки од прва три поступка врло ефикасан, док је четврти екстремно неефикасан.

Поступак вишеструком применом метода Replace делује споро у односу на друга два брза поступка, а разлог томе су чак 62 извршавања овог метода и исто толико преписивања целог стринга. Да је требало заменити доњом цртом сва појављивања само једног карактера, метод Replace би био коришћен само једном и овај поступак би вероватно био најбржи.

Главни закључак је свакако да успорење због вишеструког додељивања вредности стринговима може бити врло значајно када су ти стрингови довољно велики. Због тог успорења, поновљено просто надовезивање стрингова није прихватљиво као начин трансформисања великих стрингова.

Да бисмо још боље разумели добијени резултат мерења и његове последице, приметимо да преписивање садржаја стринга s1 при сваком додавању једног карактера у поступку D чини да је време додавања једног карактера сразмерно тренутној дужини стринга, па је укупно време рада поступка D приближно сразмерно квадрату дужине стринга. У ову законитост се можемо уверити и експериментално, извршавањем програма за различите дужине стринга који трансформишемо. Због ове квадратне зависности би поступак D, на пример, за хиљаду пута дужи стринг трајао око милион пута дуже, што је око годину и по дана, док би се време рада осталих поступака и даље мерило секундама (тврђење за остале поступке можете лако проверити).

Prethodna lekcija
Sledeća lekcija
A- A+
Тема
Темa

Prijavi problem


Obeleži sve kategorije koje odgovaraju problemu

Još detalja - opišite nam problem


Uspešno ste prijavili problem!
Status problema i sve dodatne informacije možete pratiti klikom na link.
Nažalost nismo trenutno u mogućnosti da obradimo vaš zahtev.
Molimo vas da pokušate kasnije.