ugrás a tartalomhoz

PHP - kis refaktorálás

inf · 2012. Már. 19. (H), 18.42
Sziasztok!

Van egy iteratorom, amiben a next-et szeretném szebb formára alakítani:

class TreeIterator {
    public function next() {
        if ($this->isBranch())
            $this->addBranchIterator();
        else
            $this->stepCurrentIterator();

        while ($this->hasIterator() && !$this->isIteratorValid()) {
            $this->removeCurrentIterator();
            $this->selectParentBranchIterator();
            if ($this->hasIterator())
                $this->stepCurrentIterator();
        }

        if ($this->hasIterator())
            $this->updateVisitor();
    }
    //...
}
Ami történik:
  1. ha az előző elem ág volt, akkor hozzáadjuk az iterátorát (és közben meghívjuk a rewind-ot rajta), ellenkező esetben léptetjük az aktuális iterátort a következő elemre
  2. ezek után teszteljük, hogy az aktuális iterátorunk valid e, szóval hogy létezik e rajta a következő elem (vagy ha rewind-olt iterátor-ról van szó, akkor létezik e első elem)
  3. ha nem létezik a következő elem, akkor töröljük az aktuális ág iterátorát, és átlépünk a szülő ág iterátorára, azt léptetjük a következő elemre, és megnézzük, hogy azon létezik e a következő elem, és így tovább, amíg egy olyan szülő ágat nem találunk, amin létezik a következő elem, vagy el nem fogynak az iterátorok
  4. ha létezik a következő elem (szóval nem fogytak el az iterátorok), akkor frissítjük a visitor-t (ez a bejárás adatait tárolja, egyelőre nem tudtam jobb nevet kitalálni neki)


Nekem nem állt össze fejben, hogy ebből hogy lehet tömörebb és érthetőbb kódot csinálni, az elnevezésekkel szintén gondban lennék, bármi ötlet ezzel kapcsolatban?
 
1

az elnevezésekkel szintén

Hidvégi Gábor · 2012. Már. 19. (H), 18.58
az elnevezésekkel szintén gondban lennék
Miért? Ha nem túl érthető így, használj magyar kifejezéseket ; )
2

Haha, imádom a trollkodást :D

inf · 2012. Már. 19. (H), 19.00
Haha, imádom a trollkodást :D
3

Hogy on is legyek, szerintem

Hidvégi Gábor · 2012. Már. 19. (H), 19.04
Hogy on is legyek, szerintem rendben van a kód és elég tömör, bár az az érzésem, hogy a rendszered túl van bonyolítva.

A while ciklusban picit furcsa, hogy kétszer ellenőrzöd, hogy $this->hasIterator(), azon talán lehetne spórolni valahogy.
4

Engem is ez zavar, de nem

inf · 2012. Már. 19. (H), 19.05
Engem is ez zavar, de nem tudom hogy vonjam össze. :S

Az a baja, hogy ennek igazából do-while-nak kéne lennie, mert először léptet, utána tesztel, de annak meg nem ajánlott a használata...
5

Az a baja, hogy ennek

kuka · 2012. Már. 19. (H), 19.15
Az a baja, hogy ennek igazából do-while-nak kéne lennie, mert először léptet, utána tesztel, de annak meg nem ajánlott a használata...
Ehhez van valami forrás is? Mert a PHP dokumentációban a do-while leírásánál nem említik.
7

Ja haverom egy programozós

inf · 2012. Már. 19. (H), 22.51
Ja haverom egy programozós multicégnél felügyel pár tucat emberre, ő mondta, hogy nem használják, és én elfogadtam, ennyi. (Arra már nem emlékszem, hogy pontosan miért, valószínűleg a kód átláthatóságával lehet kapcsolatban. Meg fogom kérdezni...)

Update:
Azt mondta, hogy Dijsktra szerint nem írunk hátultesztelőt, szerinte hülyeség, de betartja.
9

Köszönöm. Megnyugodtam.

kuka · 2012. Már. 20. (K), 10.28
Köszönöm. Megnyugodtam.
10

Ja megnéztem, azzal sem lehet

inf · 2012. Már. 20. (K), 15.17
Ja megnéztem, azzal sem lehet összevonni a hasIterator-os részt a ciklusban.
6

Miért nem használsz

Protezis · 2012. Már. 19. (H), 19.38
Miért nem használsz RecursiveIterator-t?
8

Semmivel nem nyújt többet,

inf · 2012. Már. 19. (H), 23.30
Semmivel nem nyújt többet, mint az én megoldásom, sőt szerintem hibás az elgondolás, amit kitaláltak hozzá:
  • A teljes fa bejárása egy magasabb absztrakciós szintű probléma, mint 1-1 ág bejárása, a RecursiveIterator-ral viszont a teljes fa bejárását és az ágak bejárását is ugyanazzal az osztállyal csináltatod meg, szerintem ez gányolás.
  • Más szemszögből nézve, ha akarsz egy olyat csinálni, hogy vegyes gyűjteményekből álló fa, és be akarsz vinni egy új gyűjteményt, akkor vért fogsz izzadni, mert ez egy struktúrált programozásra jellemző megoldás, nem pedig egy objektum orientáltra. Struktúráltban az új típust nehezebb létrehozni, objektum orientáltban meg az új metódust.
(Egyébként eddig boldog tudatlanságban éltem ezzel kapcsolatban, de ha felmerült volna, mint lehetőség, akkor is elvetettem volna.)
11

Fa, részfa

T.G · 2012. Már. 21. (Sze), 08.23
Függetlenül a kiinduló problémától, illetve a rekurzív iterátorra tett megjegyzésedtől:

Mi különbség van egy teljes fa bejárása és egy részfa bejárása között? Miért más absztrakciós szint, ha a kiinduló csúcs a gyökér elem, mint bármely más elem?
12

A teljes fához útvonal

inf · 2012. Már. 21. (Sze), 14.13
A teljes fához útvonal tartozik és mélység egy ág bejárásához meg csak kulcs és érték kell (már ha nem Set-ről van szó, mert akkor csak érték). Ahhoz, hogy bejárj egy gyűjteményt még azt sem kell tudnod, hogy az egy fának egy ága, vagy csak úgy magában van...
13

Nem teljesen értem a

Protezis · 2012. Már. 21. (Sze), 21.57
Nem teljesen értem a gondolatmeneted.

A RecursiveIterator-ból az Iterator rész lényegében a fában egy szint. Ezt bővítik ki a getChildren() és hasChildren() metódusok, amelyek minden szinten minden elemre meghívódnak (pontosabban a getChildren() csak akkor, ha a másik true-t ad vissza).

Ha neked a fában minden elemed önmagában is egy iterator, akkor meg két foreach-et egymásba ágyazol: a külső visszaadja az adott elemet (RecursiveIteratorIterator), a másik meg ezen végig iterál. Az ebben visszakapott elemek meg ugye lehetnek akármilyen típusúak, azonban ha ezt kihasználod és tényleg több minden lehet (polimorfizmus hiánya), akkor az már régen rossz.

De ha mondanál egy példát, akkor könnyebben megérteném a problémád.
14

Kérdés:A

inf · 2012. Már. 22. (Cs), 17.41
Kérdés:
A RecursiveIterator-ral hogyan jársz be egy fát (most maradjunk csak a tömbnél), és kéred le minden elemnél az aktuális mélységet és útvonalat?

Mondjuk szeretnék egy ilyen transzformációt:

array(
	'a' => array(
		'b' => array(
			'c' => 1
		),
		'd' => 2
	),
	'e' => 4
)

array(
	array(), //root
	array('a'),
	array('a','b'),
	array('a','b','c'),
	array('a','d'),
	array('e')
)
(Lehet csak én nem értem rendesen a RecursiveIterator működését...)
15

RecursiveArrayIterator

T.G · 2012. Már. 23. (P), 00.26

<?php
$tree = array(  
	'a' => array(  
		'b' => array(  
			'c' => 1  
		),  
		'd' => 2  
	),  
	'e' => 4  
);

$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($tree), RecursiveIteratorIterator::SELF_FIRST);
$tmp = array();
foreach ($iterator as $key => $item) {
	$depth = $iterator->getDepth();
	$tmp[$depth] = $key;
	$path = array_slice($tmp, 0, $depth + 1);
	echo json_encode($path), '<br />';
}
Mivel senki PHP guru nem válaszolt, ezért próbálgattam kicsit ezeket a függvényeket. Az iterátortól az útvonalat nem tudtam kinyerni, de egy segéd változó megoldotta ezt.
16

Oké, meggyőztél,

inf · 2012. Már. 23. (P), 14.25
Oké, meggyőztél, félreértettem a RecursiveIterator működését. Amit én csináltam, mint TreeIterator-t, az ugyanaz, mint a RecursiveIteratorIterator, szóval át fogok térni rá.

Path-et célszerűbb így, ha eseménykezelőt akarsz betenni a szint csökkenéshez (mondjuk html záró taget vagy ilyesmit).

$path = array();
foreach ($iterator as $key => $item) {
    $depth = $iterator->getDepth();
    $path[$depth] = $key;
    $depthReduction = count($path) - ($depth + 1);
    while ($depthReduction) {
        unset($path[$depth + $depthReduction]);
        --$depthReduction;
    }
    echo var_export($path, true) . '<br />';
}
Az a jó ebben az iterálásban, hogy nagyon könnyű vele fát transzformálni, pl:

$rootObject = new ArrayObject();
$scaffolding = array();
$scaffolding[0] = $rootObject;
foreach ($iterator as $key => $item) {
    $depth = $iterator->getDepth();
    if ($iterator->callHasChildren()) {
        $nextBranch = new ArrayObject();
        $scaffolding[$depth + 1] = $nextBranch;
        $scaffolding[$depth][$key] = $nextBranch;
    }
    else
        $scaffolding[$depth][$key] = $item;
}

var_dump($rootObject);
17

A témához kapcsolódva...

H.Z. v2 · 2012. Már. 26. (H), 13.49
Hétvégén a témád kapcsán játszottam egy kicsit pythonban a lista kezeléssel: kíváncsi voltam, kb. hogy nézhet ki egy könyvtárstruktúra, ha objektumorientált módszerrel hozom össze. (ha érdekel valakit szívesen megmutatom mind a kb. húsz sort :) )
Viszont valamire kíváncsi lennék: van-e valami ok arra, hogy ha az ember egy fastruktúrát akar modellezni, akkor ennek egyes elemei ne objektumok legyenek (pl. PHP esetében, ahol nem érvényesül a "minden objektum" elv), hanem hash(???) vagy egyszerű tömb, elemi adatokból felépítve, ahogy az itt látható példában?
Ugyanis én elkövettem azt a merényletet, hogy létrehoztam egy osztályt, ami egy belső listában azonos osztályú objektumokat tárol, így egyetlen for ciklussal végig tudok lépkedni a teljes fán. (pythonban viszonylag egyszerű így iterátort/generátort készíteni)
18

Erre leginkább akkor van

inf · 2012. Már. 26. (H), 14.47
Erre leginkább akkor van szükség, ha van egy paraméterezési módod, ami többféle osztályra igaz. Nálam mondjuk van SelectInput, MultiSelectInput, NestedSelectInput, NestedMultiSelectInput (a sima select-nél csak 1 szint lehet az optgroup), ezeket paraméterezem úgy fel, hogy label-group és label-value párok vannak a tömbben. De pl XML feldolgozásnál is előfordulhat ugyanez, vagy mondjuk ha php-ről beszélünk, akkor ott is kaphatsz bemeneten ilyen tömböt, amit át kell alakítani objektumokká, hogy fel tudd dolgozni, adatbázisból szintén lejöhetnek ilyen fák, stb...
19

Lehet, hogy félreértelek, de

H.Z. v2 · 2012. Már. 26. (H), 16.35
Lehet, hogy félreértelek, de ha mégsem: nem éri meg ezeket az egyébként egyszerű adatokat is objektummá alakítva használni?
(performancia szempontjából egyértelműen nem, de már többször le lettem ******, hogy ne akarjak előre optimalizálni :) )
Pl. esetemben semmi másra nincs szükség, mint a fájlok nevére, elérési útjára, illetve könyvtárak esetében a bennük tárolt fájlok listájára.
Végeredményben ezeket egy hash tömbben is tárolhatnám, kulcsként a nevet, értékként meg, ha könyvtárnév, akkor a benne található fájlok listáját, egyébként meg valami NULL értéket, esetleg mindenféle attribútumokat tárolhatnék, ha épp olyan a feladat.
Szóval azt próbálom megérteni, hogy hol a határ, ahol még érdemes elemi(???) adatokkal dolgozni és mikortól célszerű akár a performancia rovására is, objektumot készíteni ezekből.
20

Szóval azt próbálom

inf · 2012. Már. 26. (H), 16.49
Szóval azt próbálom megérteni, hogy hol a határ, ahol még érdemes elemi(???) adatokkal dolgozni és mikortól célszerű akár a performancia rovására is, objektumot készíteni ezekből.

Én végső soron mindegyikből objektumot készítek, viszont ezt úgy teszem, hogy kezdetnek olyan fát adok meg, amit nagyon egyszerűen le tudok írni (nem feltétlen objektumokkal, lehet sima asszociatív tömbbel, vagy XML-el), és ezt transzformálom át objektumos formára automatikusan. Ezt az objektum fát is lehet szerializálni, és visszatölteni, szóval szerintem megoldható, hogy ne menjen mindez a performancia rovására. Gondolom ha van egy xml config fájl, azt te sem olvasod be és dolgozod fel minden alkalommal, amikor kiszolgálsz egy felhasználót...
21

:)

H.Z. v2 · 2012. Már. 26. (H), 16.55
Hol élsz te? Felhasználó... egyelőre ott tartok, hogy újra ismerkedek a programozás gyönyöreivel, ennyire alapszinten, ahogy a néha hülye (vagy legalábbis annak látszó) kérdéseimből/hozzászólásaimból látszik.
Az asszociatív tömbért meg külön köszönet... szombat óta törtem a fejem, hogy azt, amit a perlben hash-nek hívnak, minek hívják más helyeken. :)