pascal-typ ukazovatel a zoznamy
mam zdrojak v pascale, takmer vlastnorucne prepisany.
ide v nom o typ ukazovatel a vytvaranie zoznamov....
Problem ale je, ze velkost pamate pred spustenim a po spusteni sa nerovna(co by sa zevraj malo).
Nakolko tomuto zatial dost dobre nerozumiem(respektive vobec), bol by niekto taky dobry a povedal mi, kde mam chybu? dik.
zdrojak najdete tu:
http://stredoslovak.szm.sk/ZOZNAM.PAS
alebo:
http://stredoslovak.szm.sk/ZOZNAM.rar
Na zaciatku tvorby tohto programu som to skusal kadejako editovat a vsimol som si, ze ked som tam nepouzil prikaz premenna:=nil, tak potom sa velkost pamete pred a po rovnala. lenze prikaz nil tam pouzivam a neviem, ci sa to vobec da spravit bez neho.
dik za odpovede.
Za hlavným begin:
[pas]begin
clrscr;
a:=memavail;
new(zac);
new(kon);
zac:=nil;
kon:=nil;
repeat
clrscr;
writeln('1-vloz na koniec');
writeln('2-vloz na zaciatok');
writeln('3-vypis');
[/pas]
Uprav to takto:
[pas]begin
clrscr;
a:=memavail;
zac:=nil;
kon:=nil;
new(zac);
new(kon);
repeat
clrscr;
writeln('1-vloz na koniec');
writeln('2-vloz na zaciatok');
writeln('3-vypis');
[/pas]
A toto je vlastne zbytočné:
[pas] zac:=nil;
kon:=nil;
[/pas]
Ešte sa pozriem na ten zvyšok.
Edit: Ešte som zabudol: Keď vytvoríš pamäťové miesto (funkcia New()), tak nesmieš priradiť premennej inú hodnotu, lebo to miesto ostane v pamäti alokované, ale nepoužiteľné a v prípade behu skopilovaného programu mimo Pascal sa ti môže po čase zrútiť systém na nedostatok pamäti v prípade veľký pamäťových blokov.
Žiaľ tam je chýb viac. Ty nesmieš akolovať pamäť pre smerníky, ktorým priradíš hodnotu nejakého smerníka. Takisto ju nesmieš ani dealokovať. Ty to robíš tak, ako keby kúpiš auto (alokuješ pamäť) a potom svoje necháš na ulici a požičiaš si susedove (priradíš hodnotu iného smerníka). Keď raz kúpiš auto (vytvoríš smerník) a používaš len to jedno (iný smerník so záznamami nepoužívaš) nepotrebuješ iné auto, čiže nemá zmysel kupovať ďalšie (načo alokovať miesto pre ďalšie smerníky?). Ide o to, že ty len obsah smerníka uložíš do druhého (to ako keby si požičal auto niekomu ty), čiže on to auto už kupovať nemusí (nemusíš alokovať miesto pre smerník).
Tipujem, že si mi nerozumel o čom píšem. Ostatní programátori dúfam áno. Ty si musíš smerníky poriadne ešte naštudovať, lebo máš v tom poriadny zmätok.
...tak to si ma moc nepotesil
Vo stvrtok z tohto pisem pisomku a o tyzden v pondelok treba odovzdat semestralku....esteze tomu nechapem
Ty tam proste robíš s jedným poľom údajov. Takže new() použiješ len raz. Ostatné smerníky nastavuješ podľa tohto prvého, takže všetky zbytočné new() a dispose() musia preč. Pri hľadaní tam to dispose() nemá čo hľadať. Tým si iba dealokuješ to, čo si si alokoval. A vždy keď vytvoríš nový prvok poľa, tak použiješ new(). Potom samozrejme, keď končí program, tak musíš použiť dispose() pre každý prvok poľa.
Edit: Už asi 2 alebo 3 týždne chápem tomu, čo raz dávno povedal Jan Fiala a to, že v školách sa používajú zastaralé metódy. V Delphi nikdy nikto nepotrebuje vedieť ako sa robí so smerníkmi pri vytváraní databáze. Vtedy som sa hádal, že smerníky treba pochopiť, ak chce niekto vedieť programovať, ale teraz viem, že toto je zbytočné (myslím takúto úlohu, ako má dotyčný). Pascalu v DOSe už odzvonilo. Dokonca som uvažoval aj o vytvorení vlákna na túto tému.
V osnovách přetrvává styl programování v assembleru, který se používal tak před 20 až 30 lety (nepřímá adresa s bází v registru). Dnes skutečně není pro používání směrníků (ukazovátek, pointerů) rozumný důvod. Kdyby je raději učili používat objekty.
Ako hovoríš. Tiež si to myslím. Objekty dnes vládnu svetu. V DOSe v Pascale by sa mali učiť akurát tak priebeh programu, aby vedeli, ako bude fungovať spracovanie metódy OnClick (ľaľa, ono to tu ide bez problémov). Oni iba strácajú čas sprostosťami. Na strednej by sa mali už učiť napríklad protokoly a sieť a oni sa učia Word a Excel. Škoda, že nie len v osnovách programovania je to zaostalé, ale aj celej informatiky.
Já už se v našem školství nedivím ničemu. Napodzim jsem opravoval známé učitelce v základní škole síť. Já nevěřil vlastním očím! Učili je Norton Commander a Text602 ! Já na to jářku, co takhle Windowsy a Word? Prej (vraj) tomu tady nikdo nerozumí. Že mají jen učitele, kteří si na víc netroufnou, aby se před dětma neztrapnili! To je úroveň... Éééch, škoda slov...
Dovolim si zasadne nesuhlasit s tvrdenim "Dnes skutečně není pro používání směrníků (ukazovátek, pointerů) rozumný důvod".
Pouzitie smernika je v mnohych pripadoch efektivnejsie ako ine moznosti ktore dany programovaci jazyk ma.
P.S. "pouzivat objekty" je v podstate pouzivanie smernikov, len je to pekne "zabalene" (objekt je v podstate len jedna kopa smernikov plus kod a data samozrejme ;))
a ja si dovolim s nazorom MM.. plne suhlasit. smernik je velmi silna vec v rukach znaleho cleveka a velmi zradna, v rukach neznaleho.
Aj ja súhlasím, ale robiť databázu s variabilnou dĺžkou cez smerníky je v dnešnej dobe blbosť. Stačí pochopiť princíp smerníka.
Ja neviem co sa tam snazil robit, ani ake mal zadanie (nestudoval som poriadne otazku), ale nesnazi sa on napr. robit spojkovy zoznam, alebo obojsmerny spojkovy zoznam? To je jedna zo zakladnych datovych struktur a pouzitie smernikov je tam NUTNE. Je normalne ze sa v skole snazia studentom vysvetlit datove struktury, aby aspon chapali co program bude robit ked sa pouzije napr. objekt typu "List" (nejaky je snad v kazdej kniznici) a aby vedeli ako to pouzit efektivne (resp. ako efektivne ukladat data v praxi). Spojkovy zoznam ma svoje vyhody a v urcitych pripadoch (ak chces implementovat odstranenie prvku zo zoznamu) je jeho pouzitie "nutne" (ostatne sposoby by boli extremne neefektivne, rozumej milionkrat pomalsie :).
Kedze to ludia nechapu, tak potom musim s hrozou zazivat "programy" ktore napr. ked chcu pracovat so zoznamom 100 000 suborov (zoznam suborov na vsetkych CDckach) tak kliknem na nejaky button ze odstranit nieco z toho a ten program pracuje pol minuty (zazil som uz taky "skvely program"! ). Ta ista operacia je ale urobitelna v zlomku sekundy, ak by "programator" vedel aspon co to je spojkovy zoznam.
Databaza je nieco ine, omnoho komplikovanejsie, nie vzdy je nutna databaza. (podobne ako si nejdem kupit kamion ked chcem previezt zapalkovu krabicku).
Lenže na čo to robiť takto, keď v jazyku Object Pascal sa to dá urobiť rýchlejšie? Tkéto programy sú náchylné na drobnú chybičku s rizikom zrútenia systému. Alebo aj ty by si v objektovo orientovanom jazyku použil takúto možnosť, keď mám možnosť použiť objekty stvorené práve na takú činnosť?
V skole nejde o to urobit nieco rychlejsie, ale o to aby studenti pochopili ake a naco su jednotlive datove struktury, ako prakticke zadanie pre studenta sa k tejto teme nuka prave naprogramovat spojkovy zoznam (t.j. taky objekt aky by si ty v objektpascale pouzil, tiez ho niekto musel naprogramovat).
Skola ktora by studentovi povedala: "ak chces vytvorit zoznam tak pouzi CList.Create()" by bola zla skola, ptz akonahle by student sadol k napr. devcpp tak by bol v prd***. Ak vie co to je list a na co to je tak si to vie najst za 10sekund v helpe daneho prostredia/kniznice ktoru pouziva a nema potom problem programovat v akomkolvek prostredi (od pascalu az po Javu alebo C#).
A 100 000 prvku ve spojovem seznamu ma byt jako efektivni reseni? To snad ne.
Ja som netvrdil ze si to musis alokovat po jednom, ani ze mal pouzit spojkovy zoznam, tvrdim ze program ktory kvoli jednoduchej operacii je pol minuty neovladatelny lebo "si nieco robi" je totalny paskvil.
P.S. tak navrhni lepsiu datovu strukturu napr. na rozne velke zaznamy.
Dobre, tak to jsem te asi jen spatne pochopil.
Tezko navrhovat konkretni datovou strukturu, kdyz nevim, jake jsou na ni kladene pozadavky. Ale napriklad bych pouvazoval, jestli neni vhodne pouzit nektery z mnoha typu stromu, nebo treba hash tabulku. Dlouhe spojove seznamy jsou neefektivni i kvuli tomu, jak jsou nepratelske vuci cache.
to msx.
tak som na to pozeral a na nic som neprisiel.
V podstate som si upravil iba prednasku, ktora je dostupna tu:
BTW. ked to prehodim tak ako si to spravil, funguje to, ale iba ked spustim program a zaroven ho aj skoncim. Keby som s programom pracoval dalej, tak uz to nefunguje, lebo ja tam pororvnavam, ci maju hodnotu nil alebo nie.
O hodinu, max. dve sa na to pozriem, čo je to zač ten link. Teraz nemôžem.
nasiel som nejaky program, tak som ten moj podla neho poupravoval a vyzera, ze uz to ide(=velkost pamate pred a po sa rovna). Este tam musim napchat pracu so suborom a pomaly budem mat semestralku.
nova verzia je tu:
Skoro je to dobře, až na ty poslední 4×Dispose (za cyklem repeat). Překvapuje mě, že ti to tam nevypíše chybu, uvolňuješ již uvolněnou paměť.
jj, to som si vsimol, som to odstranil ale az po tom, ako som to nauploadoval.
Freepascal mi chybu nehadzal.
teraz mam ale problem s nacitanim zo suboru. Ak chce niekto poradit, tak tu mate zdrojak-6 je ulozenie do sub, 7 - nacitanie zo sub. ulozenie mi asi ide(nieco sa do suboru ulozi), ale nacitanie nie.
ak to bude niekto skusat, tak si tam este pridajte assign a rewrite.
[pas] '6': begin
reset(f);
pom:=zac;
while pom<> nil do
begin
writeln(pom^.meno);;
write(f,pom);
pom:=pom^.dalsi;
end;
close(f);
readln;
end;
'7': begin
reset(f);
read(f,zac);
while not eof(f) do
read(f,pom);
close(f) end;[/pas]
skusal som to aj tak, ze som si chcel najprv nacitat zac(prva polozka v sub),potom kon(posledna polozka) a potom som chcel citat zo sub pokial sa zac <> kon. Problem ale je vobec to nacitanie hocicoho zo suboru.
Neverím, že ti uloží správne údaje (teraz ti to ukladá hodnotu smerníka), pretože ti tam chýba znak ^ v riadku:
[pas]write(f,pom^);[/pas]
Toto som napísal len na rýchlo. Takýmto spôsobom uložíš obsah celej premennej aj s odkazom na ďalší prvok, ktorý je v uloženom súbore zbytočný. Malo by to teda vyzerať takto:
[pas]write(f,pom^.meno);[/pas]
No a k čítaniu: Keď máš ukladanie takéto (aj tu vidím chyby):
[pas] rewrite(f); {opravil som reset na rewrite}
pom:=zac;
while pom<> nil do
begin
writeln(pom^.meno);
write(f,pom^.meno); {opravil som to tak, ako som spomenul vyssie}
pom:=pom^.dalsi;
end;
close(f);
[/pas]
Tak čítanie je presný opak:
[pas] reset(f); {citame, takze reset}
new(zac);
pom:=zac;
if not eof(f) then {ochrana proti prazdnemu suboru)
repeat {cyklus s podmienkou na konci, while tu nie je vhodne}
if pom <> zac then {ak nie sme na zaciatku, tak vytvorit nove pamatatove miesto}
begin
new{pom^.dalsi);
pom:=pom^.dalsi;
end;
read(f,pom^.meno); {nacitat}
writeln(pom^.meno);
until not eof(f); {toto opakukovat do konca suboru}
close(f);
[/pas]
S tím ukazatelem má msx. pravdu. Jeho kód bych přesto lehce upravil:
[pas]rewrite(f); {opravil som reset na rewrite}
pom:=zac;
while pom<> nil do
begin
writeln(pom^.meno);
writeln(f,pom^.meno); {opravil som to tak, ako som spomenul vyssie}
writeln(f,pom^.priez); {rulda: kazdou polozku na samostatny radek, jinak meno+priez pozdeji nacte jako jednu polozku}
pom:=pom^.dalsi;
end;
close(f);
[/pas]
Načítání by potom vypadalo takto:
[pas]while zac<> nil do zrus_prvy; {ochrana proti nasobnemu nacitani}
reset(f); {Assign uz Pavel udelal}
while NOT Eof(F) do {ja bych se while nebal}
begin
Readln(f, meno1); {kazda polozka je na samostatnem radku.}
if NOT Eof(F) then Readln(F, priez1)
else priez1:= '';
novy_kon(meno1, priez1);
end;
close(f);
[/pas]
PS: snad se msx. neurazi za przneni jeho kodu
/edit. Drobna oprava chyby
dik, nejak som to spravil-inac volanie funkcie novy_kon bol dobry napad-asi by som to nepouzil a robil tak ako je to vyssie.
tiey som si to komplikoval tym, ze som mal file of smernik-zmenil som to na file of string-je to jednoduchsie.
tu je vysledok ukladania a nacitavania zo sub:
[pas] '6': begin
reset(f);
pom:=zac;
while pom<>nil do
begin
write(f,pom^.meno);
write(f,pom^.priez);
pom:=pom^.dalsi;
end;
close(f);
writeln('Hotovo');
readln;
end;
'7': begin
reset(f);
zac:=nil;
while not eof(f) do
begin
read(f,meno1,priez1);
novy_kon(meno1,priez1);
end;
close(f);
writeln('Hotovo');
readln;
end[/pas]
Dza rady, zajtra to hadam nejako dokoncim(generovanie nahodnych mien a priezvisk-to uz mam, iba to vlozit).
msx. a Rulda ak pouzivate warforum, tak napiste vase nicky, poslem vam aspon nejake WM za pomoc.
A na koniec este jedna otazocka:
Pridavanie na urcite miesto a mazanie urciteho miesta(podla pozicie,napr. niekde v strede) by bolo asi zlozite spravit?
Dobrý, ale mám 3 výhrady:
1) při zápisu souboru použij Rewrite místo Reset (už na to upozorňoval msx.)
2) při načítání neuvolníš starý seznam, který je stále v paměti.
3) proč používáš file of string? Sice to nejspíš funguje, ale soubor bude veliký (počet_záznamů × 2 × 256 Bytes).
Předpokládal jsem použití f: text , tedy klasický textový soubor. Potom samozřejmě musíš použít verze Readln a Writeln místo Read a Write (jak jsem psal výše)
Přidavání a mazání uprostřed: pokud myslíš v souboru, tak jednoduchým způsobem ne. (Ale snadno lze vytvořit (přepsat) nový soubor s novými/bez smazaných záznamů). Pokud myslíš v seznamu (v paměti), tak ano. Prostě si dokrokuješ před požadovanou položku a:
mazání:
[pas]{v pom je polozka pred(!) mazanou polozkou}
temp := pom^.dalsi; {temp bude vymazan}
pom^.dalsi := temp^.dalsi; {navazani seznamu, aby nevznikla dira}
Dispose(temp);[/pas]
pridani:
[pas]{v pom je polozka pred vkladanou polozkou}
New(Temp);
Temp^.Dalsi := pom^.dalsi;
Pom^.Dalsi := Temp;[/pas]
kod je zjednoduseny, napr. neni osetren pripad, kdy pri mazani bude pom^.dalsi = nil.
Áno, s tým novým riadkom máš pravdu, takže oprava chyby bola nutná. Ja som to "kompiloval" totiž v hlave, hoci Turbo Pascal na disku mám a to plne funkčný (aj fix 200 MHz pre runtime error 200), pretože by bol hriech nemať taký skvelý program na disku.
Najprv si sadni nad prednasky/skripta alebo si daj do google "linked list" a snaz sa to pochopit. Az to pochopis nebudes mat ziaden problem to naprogramovat (je to trivialna zalezitost). Slusny programator by mal mat jasno v datovych strukturach, nech neprodukuje nepouzitelne paskvily.
Napr. Linked_list
uz to mam takmer hotove:
este by ma zaujimalo-automaticky tam generujem(najprv do suboru a potom to nacitam), a ked si tam niekto da velky pocet, ako to mam skontrolovat, ci sa to vojde do pamate?
myslim...-neviem ci mam pouzit funkciu maxavail(vrati velkost najvacsieho volneho bloku) alebo memavail(vrati velkost vsetkych volnych blokov), respektive, mam tam vobec pouzit zistovanie, ci je dost volneho miesta v pamati?
memavail()
Ty potrebuješ zistiť voľnú pamäť, nie najväčší blok. Najväčší blok je vtedy potrebný, ak alokuješ naraz veľa pamäti a potrebuješ vedieť, či taký blok existuje ako celistvý.
Hezký. Čtení náhodně generovaných jmen mě pobavilo
Na první pohled se to zdá v pořádku (ale komu by se chtělo přemýšlet v neděli odpoledne, že). Nějaké kosmetické změny bych ti ale doporučil (i když na výslednou funkci nebudou mít vliv)
- proměnná kon je IMHO zbytečná. Alespoň jsem si nevšiml, že bys ji někde potřeboval. Jen ti komplikuje život, protože ji při editaci seznamu musíš aktualizovat.
- v hlavním programu je příliš mnoho kódu. Je zvykem rozsáhlejší části kódu dávat do samostatných procedur (podle významu). Např. pouze část pro výpis seznamu má 5 stran (obrazovek). Ta by se určitě lépe vyjímala v samostatné proceduře.
... to be continue ...
- když už jsme u toho rozsahu, tak se mi vůbec zdá, že kódem nešetříš. Proč např. u zmiňovaného výpisu vypisuješ zvlášť první stranu a ty další? Nestačilo by udělat takhle?
A ještě k té paměti. Msx. se bude zlobit, ale já mám jiný názor. Jedna položka v seznamu (v tomto případě) má 516 Byte (2×string + pointer). Pokud bude veškerá zbývající paměť (řekněme 50kB) roztroušena v blocích < 516 Bytes, tak alokace další položky selže, přestože celková dostupná paměť (součet všech bloků) bude víc než dostatečná. Proto bych se přikláněl k MaxAvail.
Hnevať sa nebudem vlastne máš pravdu, totiž, ak bude voľné miesto na alokovanie dostatočnej pamäte, tak maxavail() dá vždy nejaké vysoké číslo, ak to číslo bude menšie ako potrebuje, tak to znamená, že tú pamäť má už plnú.
...premennu kon pouzivam pri pridavani polozky na koniec.
Prirobim tam teda este procedury.
I:5,':', pom^.meno:11,-toto nepouzivam, lebo sa mi to zarovna do prava a to nevyzera tak dobre ako ked je to zarovnane do lava(=preto gotoxy).
A k tomu co tu pises, nam vraveli, ze v skolskych programoch nemame pouzivat "nasilne prerusenia"-ci uz break, halt alebo goto.
A pridal som tam aj memavail(skusal som aj maxavail)-v oboch boli velkosti pridanych poloziek rovnake-dovolilo pridat max 3971 prvkov-je to OK?
jj. S tím kon máš pravdu.
Pokud chceš zarovnávat doleva, jde to udělat pomocí Lenght(vrací počet znaků řetězce) např. takto:
Break není násilné přerušení. Pouze předčasně ukončí cyklus (For/repeat/while) a program pokračuje následujícím příkazem za cyklem. Nepatří do stejné skupiny jako příkazy Goto či Halt.
MemAvail a MaxAvail vracejí shodné hodnoty, pokud nedojde k fragmentaci paměti (t.j. dokud je veškerá dostupná pamět v souvislém bloku). To je sice pravidlem po spuštění programu, ale po delší práci (alokování/uvolňování různě velkých položek) se paměť může fragmentovat. Potom obě funkce vracejí různé hodnoty. (MaxAvail <= MemAvail)
3971 položek - to závisí na dostupné konvenční paměti. Bude se tedy lišit při spuštění na různých počítačích, a bude se zmenšovat, jak tvůj program poroste. Pokud je to málo, budeš muset požít nějaký úspornější formát (String zabere v paměti 256 Bytes nezávisle na tom, kolik "užitečných" znaků obsahuje)