Pascal recordok

Fórumok

Üdv!

Előkerült egy probléma, aminek nem tudom, van-e megoldása.


type csalad=record
nev:string;
gyerek:byte;
end;
var cs:array[1..100] of csalad;

Most szeretnék írni ehez egy rendezést, aminek paraméterként adom át, hogy mi szerint rendezze. Pl. rendez(nev). Az egyetlen működő megoldás az, hogy a rendezésnél egy feltétell megvizsgálom, mit adtam meg (case) és a szerint adom meg utána a rendezésben szükséges feltételt. Kb így:


case mit of
'nev': if cs[i].nev>cs[j].nev then
begin
[...]
end;
'gyerek': if cs[i].gyerek>cs[j].gyerek then
begin
[...]
end;
end;

Ez a megoldás szerintem nagyon gány, nincs erre a problémára elegáns megoldás?

Hozzászólások

Ha általánosan akarod:


type relacio=function(cs1,cs2:csalad):boolean;
procedure rendez(mit:relacio);
begin
[...]
if mit(cs[i].nev,cs[j].nev) then
begin
[...]
end;
[...]
end;

A gyerekek rendezéséhez ezt kell átadni:


function gyereknagyobb(cs1,cs2:csalad):boolean;
begin
result:=cs1.gyerek>cs2.gyerek;
end;

Nem ellenőritem, lehet benne bug.

Amúgy a nevek összehasonlításához inkább a sysutils függvényeit kéne használni, amik figyelnek a kódolásra, tehát az ékezeteseket is jól rendezik.

Ha jól értem, a mit az = relacio, ami pedig visszatér true-val, ha cs[i].nev>cs[j].nev. És ez adódik vissza if-nek. Szóval ha visszafejtjük, ezt kapjuk az if-be: cs[i].nev.gyerek>cs[j].nev.gyerek. Vagy rosszul értelmezem?

Megpróbálom leírni a "feladatot" kicsit máshogy, hátha csak félreértés van. :)

Szóval van a fent leírt cs tömb. Ez feltöltve pl. így néz ki:
cs[1].nev='Szabó';
cs[1].gyerek=2;
cs[2].nev='Kovács';
cs[2].gyerek=1;
cs[3].nev='Nagy';
cs[3].gyerek=3;

És a feladat az, hogy ha nekem úgy tetszik, akkor név szerint rendezem, ha meg úgy, akkor gyerek szerint.
Tehát ezeket kaphatjuk:
1. Kovács 2
2. Nagy 3
3. Szabó 1

Vagy

1. Szabó 1
2. Kovács 2
3. Nagy 3

Namost. Ez ugye egyszerűen megoldható, ha 2 eljárásunk van, amelyikek közül az egyik rendezi név szerint, a másik meg gyerek szerint. Viszont ez nem elég rugalmas, mert mi van akkor, ha hirtelen be kell hoznom egy 3. paramétert, pl. jövedelem. Ezért arra gondoltam, fogok egy rendezést, aminek átadom paraméterben, hogy mi alapján rendez. Ha a pascal nagyon rugalmas lenne, ez akár így is kinézhetne:


procedure rendez(mit:string);
begin
[...]
if cs[i].mit>cs[j].mit then
[...]
end;

begin
[...]
rendez('nev');
[...]
rendez('gyerek');
[...]
end.

De mivel a nyelv nem scriptnyelv, ezért ez a megoldás járhatatlan.

Eljárás típust használtam. Valóban nem túl rugalmas. Esetleg használhatsz variant tömböt, de ez növeli a memóriahasználatot és valószínűleg csökkenti a sebességet. Vagy mutatótömböt, ekkor csak a típust kell valahogy átadni, vagy egy típus-pointer recordban tárolni, de figyelni kell a mutatók kezelésére. Továbbá lehet valamilyen lista objektumokat[/url] használni, valamilyen absztrakt osztályból származó objektumokat tárolni, amiket egy absztrakt metódus implementációja hasonlít össze másik azonos típusú objektummal, vagy Free Pascal esetén operátor felüldefiniálást használni.

Szóval mindegyik megoldás bonyolult, akkor értemes használni, ha tényleg szükség van az általánosságra.

Hello!

Egy eleg hulye megoldas jutott eszembe. Ha a ket adatot egy stringben helyezned el akkor a relacio mindig annak megfeleloen rendezne oket, hogy mi van a string elejen. Kerdes az, hogy megengedhetsz ilyen feltoltest vagy csak tovabb ganyolja az egeszet.
Ami biztos, hogy semmikeppen sem kene minden iteracioban lefuttatni egy if-et arra, hogy mi alapjan rendezel, akkor inkabb csinalj ket ciklust kulon.

pascalban eljárás is lehet változó. a rendező eljárásnak egyszerűen átadod az összehasonlító eljárást, és aszerint lesznek rendezve a recordjaid.

"pascalban eljárás is lehet változó"

Ez eddig világos. Nade hogy adom át és hogy használom? Kaphatnék egy példát?

Pl. egyszerűség kedvéért most csak kiíratni akarom a tömböt egy ciklussal.
Szóval ezt kellene átírni úgy, hogy a nev ne konstans legyen, szóval ha én a gyereket akarom kiírni, az úgy adódjon át. Akkor talán meg is értem. :)


procedure kiir;
begin
for i:=1 to n do
begin
writeln(cs[i].nev); {Ezen kellene módosítani}
end;
end;

Ezt ugye így hívom meg: kiir; Kb egy hasonló, egysoros megoldást szeretnék kapni, amiben én adom meg, melyiket (nev vagy gyerek) írja ki.


...
type
TCompareFunc = function(A, B: record):integer;
function Compare(A, B: record): integer;
begin
// Itt megírod az összehasonlításhoz a kódot, és pl ha A>B akkor result := -1, ha egyenlő akkor 0, ha kisebb 1
end;
procedure Rendez(T: array, CF: TCompareFunc)
begin
//Itt meg tudod hívni a CF fgv-t, ettől függően rendezed a tömbödet
KissebbVagyNagyobb := CF(X,Y);
end;
var
func: TCompareFunc;
Lista: array;
begin
func := @Compare;
Rendez(Lista, func);
end.

Csak nagy vonalakban.

Ok, én még mindig nem értem. :)

Azt értem, hogy van a Compare fv, ami elvégzi az összehasonlítást. De amikor meghívom a CF fv-t, akkor nekem már tudnom kell, hogy a nev vagy gyerek alapján rendezek. Legalábbis így látom. Viszont ahhoz a Rendez-nek kellene átadni, hogy én nev vagy gyerek alapján rendezek. Jó lenne, ha látnék egy konkrét példát a fenti feladat esetében, hogy hogy kellene meghívni a dolgokat ahhoz, hogy jól működjenek...

Érdekes, hogy ez scriptnyelvekben milyen egyszerűen megoldható. Bash-ban pl. ennyi: ${cs[i]}.($mit) Mármint így nézne ki, ha lennének recordok bashban. :) Js-ben meg pl. ennyi: eval('cs[1].'+mit); Persze js-ben sem tudok recordokról...

A pascal típusos nyelv, azt nem várhatod el tőle, hogy kitalálja mit, hogyan akarsz rendezni.
Esetleg adhatsz át pointereket, meg változó hosszakat, de így csak bináris rendezést tudsz csinálni.
Talán variant típussal is megoldható, de annak használatába még nem kellett belemerülnöm. :)

Huhh, essünk neki mégegyszer. :)

van egy tömb, aminek minden eleme 1 1 record. Pl.


cs[1].nev='Szabó';
cs[1].gyerek=1;
cs[2].nev='Kovács';
cs[2].gyerek=2;
cs[3].nev='Nagy';
cs[3].gyerek=3;

Most nekem van egy menüm, amiben kiválasztom, hogy ezeket az értékeket név szerint, vagy gyerekek száma szerint rendezem. Tehát ha a név szerinti rendezést választom, ezt akarom kapni:


Név    Gyerek
-------------
Kovács 2
Nagy   3
Szabó  1

De ha én gyerekek száma szerint akarok rendezni, ezt szeretném kapni:


Név    Gyerek
-------------
Szabó  1
Kovács 2
Nagy   3

Namost. Választhatom azt a megoldást, hogy csinálok 2 eljárást, az egyik rendez név szerint, a másik gyerek szerint. De ez így elég gazdaságtalan, mert a 2 rendezés gyakorlatilag ugyan az a kód, csak az egyikben a nev szerepel cs[számlálóváltozó] után, a másikban a gyerek.

Más nyelvekben, ahol nincsenek recordok, ott mátrixszal oldom meg a problémát. Pl.


/*Igen, volna 0. elem is, ha C-ről beszélünk. :)*/
cs[1][1]='Szabó';
cs[1][2]=2;
cs[2][1]='Kovács';
cs[2][2]=1;
cs[3][1]='Nagy';
cs[3][2]=3;

Amit ezután kb így rendezhetek:


void rendez(int mit)
{
[Rendezés eleje, feltétel:]
if(cs[i][mit]>cs[j][mit])
[Rendezés vége]
}

És így hívom meg, ha név szerint akarok rendezni:

rendez(0);

, és így, ha gyerek szerint:

rendez(1);

.

Ez a megoldás pascalban is működne, de mivel egyrészt szebb, ha használok recordokat, másrészt éppen a recordokat tanuljuk, nekem úgy kellene megírni a programot. Szóval erre keresek megoldást.

Nem ugyanaz, ez tény. Viszont elég jó eredményt ad vissza ahhoz, hogy példaprogramként megoldjuk órán. És én ezt gondoltam volna tovább. Kár, hogy nem megoldható.

Illetve órán még eszembejutott egy olyan megoldás, hogy egy vektorba kiszórom a record azon elemeit, amiket rendezni szeretnék. Ezután a vektoron megyek végig, és amikor a vektorhoz nyúlok, akkor a cs tömböt is módosítom. Persze itt meg az a probléma, hogy a record többféle változót tartalmazhat. Ha tudnám, hogy hogy lehet változót dobni, majd futás közben újra létrehozni, megoldható lenne a probléma. (Ez utóbbi egymásba ágyazott eljárásokkal megoldható.)

A

Rendez(Lista, func);

hívásnál a második paraméter tartalmazza a rendező függvényt. A

func := @Compare;

értékadás után ugyanis a func változó a Compare függvény memóriacímét tartalmazza. Ez a változó a Rendez függvényben CF néven jelenik meg, így az ottani

CF(X,Y)

függvényhívás a Compare függvényt hívja meg, ami itt az egyik rendező függvény. Másik rendező függvény használatához a Rendez második paraméterében a másik függvény címét add át. Amúgy azt hiszem, mem kell a függvény címét változóba írni, közvetlenül

Rendez(Lista, @Compare);

is jó.

Amúgy ha nem kell valami speciális gyors rendezési algoritmus (azaz O(length(Lista)^2) is elég), legegyszerűbb mindehol kiírni a rendezési algoritmust (kb. egy sor). Ha O(length(Lista)*log(length(lista))) idejű kell, akkor jobb, ha osztályokkal dolgozol.