2024. május 5., vasárnap

Gyorskeresés

PHP iteráció

Írta: | Kulcsszavak: Iterator . IteratorAggregate . ArrayIterator . foreach . php

[ ÚJ BEJEGYZÉS ]

PHP-val képesek vagyunk megoldani azt, hogy egy-egy objektumunk iterálható legyen foreach-csel, avagy kompletten tömbként működjön. Most vegyük az egyszerűbbet, és nézzük meg, hogyan kell iterálható osztályt írnunk:

http://php.net/manual/en/class.iterator.php

Készen is vagyunk, örülünk.

De.

A PHP SPL könyvtárában akad egy olyan varázslat, amit IteratorAggregate-nek hívunk. Ez szintén segít megoldani a problémánkat, csak sokkal egyszerűbben. Itt csak egy metódust kell implementálnunk, ami visszaad egy ArrayIterator-t. Ugye, hogy sokkal szebb?!

De.

Van egy másik hatalmas előnye a második verziónak: gyorsabb.
Készítettem egy házitesztet, aminek az eredménye ez lett:
IteratorAggregate: 12,5+ másodperc
Iterator: Fatal error: Maximum execution time of 200 seconds exceeded in D:\xampp\htdocs\ref\index.php on line 26

Nem tudom, hogy vagytok vele, de én frissítem a kódjaimat.

Hozzászólások

(#1) Peter Kiss


Peter Kiss
senior tag
LOGOUT blog

[ értesítő ]

(#2) Sk8erPeter


Sk8erPeter
nagyúr

Ezt is jó tudni, köszönjük!
Én személy szerint kíváncsi lennék a gyakorlati használatára is - a saját tesztedet esetleg nem publikálod?
Ugyanezt akkor lehetne alakítgatni egyéni tesztek írására is.

Btw. örülök, hogy valaki itt PH!-n haladó módon is foglalkozik a PHP-val, és végre nem csak olyan színvonalú írásokat látni, hogy "hogyan validáljunk és dolgozzunk fel egy formot szerveroldalon PHP-val". Így tovább!

[ Szerkesztve ]

Sk8erPeter

(#3) modder válasza Sk8erPeter (#2) üzenetére


modder
aktív tag

gyakorlati hasznát én ennek pl. a Kohana ORM moduljában láttam, ahol egy lekérdezés pl.:
ORM::factory('Entitas')->find_all()
egy iterálható objektumot ad vissza entitásokról, és végig lehet rajta menni foreach-csel. A háttérben pedig a mysql result set-ből dolgozik. Az értelme valami olyasmi, hogy nem alakítja az összes adatbázis sort objektummá, csak akkor, amikor konkrétan el akarod érni.

(#4) Sk8erPeter válasza modder (#3) üzenetére


Sk8erPeter
nagyúr

Ezt jó tudni.
Egyébként a korábbi hsz.-emben arra céloztam, hogy kíváncsi lennék, hogyan nézett ki Athlon64+-nál az összehasonlítás (maga a kód), de rájöttem, hogy a hivatalos oldalon mutatott IteratorAggregate-re mutatott példa annyira egyszerű, hogy túl szép, hogy igaz legyen. :D

..............
public function getIterator() {
return new ArrayIterator($this);
}
..............

Hát ez ilyenformán nem egy nehéz valami.
Gondolom ilyenre lefuttatott egy buzinagy ciklust egy kis mérőkével, meg a másikra is, és kész.

(#1) Athlon64+ :
"[értesítő]"
így nem egyszerűbb? >>
:P

[ Szerkesztve ]

Sk8erPeter

(#5) Peter Kiss válasza Sk8erPeter (#2) üzenetére


Peter Kiss
senior tag
LOGOUT blog

Megoszthatom a tesztet, de nem akarok nevetség tárgya lenni, annyira faék. :DDD

Gyakorlati használata rém egyszerű (egyszerű példában persze :DDD ):

class IT implements IteratorAggregate {

protected $_a = array();

public function getIterator() {
return new ArrayIterator($this->_a);
}

}

TableEntity-mben:
abstract class TableEntity extends \System\ObjectBase implements \IteratorAggregate, \System\Collections\IEnumerable {

/* ... */

public function getIterator() {
return new \ArrayIterator($this->ToArray());
}

}

---

Amint kiküldöm az első hozzászólást, kapcsolok is rá a borítékra. ;)

(#6) Peter Kiss válasza modder (#3) üzenetére


Peter Kiss
senior tag
LOGOUT blog

Nem hiszem, hogy ilyen varázslat lenne a Kohana-ban. Túlságosan bonyolítaná a dolgokat, ha nem fetchelnék objektummá azonnal az elemeket, mondom ezt úgy, hogy építettem ORM-et. Az előző hozzászólásomban írtam, hogyan pakoltam bele az enyémbe, annál fogva:

foreach ($dataContext->Members() as $member) {
/* ... */
}

Gyakorlatban használok ilyet mindenféle container (EntitySet, ObjectGroup, Enumerable, stb) bejárására, de pl. a Config osztályom is tudja, ott a gyerek elemeket lehet bejárni (XML-ből táplálkozik). De pl. a saját session-kezelésből adódik egy ACSession osztályom, aminél a $_SESSION-t tudom bejáratni, ha ahhoz van kedvem.

(#7) Sk8erPeter válasza Peter Kiss (#6) üzenetére


Sk8erPeter
nagyúr

Attól még, mert írtál ORM-et (ami önmagában azért nem varázslat, még ha szopó feladat is lehet), egy másik framework miért ne működhetne másképp, mint ahogy Te megírtad?
modder ismeri a Kohanát, dolgozott vele, arra alapozva mondja.
Azt mondjuk nem értem, Te mire alapozva cáfoltad, hogy nem úgy működik, ahogy ő írta, ha ezek szerint meg sem nézted. :)

Én nem ismerem a Kohanát, ezért vettem rá a fáradságot, hogy letöltsem a keretrendszert és megnézzem, mi lehet az igazság, itt van kimazsolázva az érintett kódrészlet:

class Kohana_ORM extends Model implements serializable {

//...................................................

/**
* Database query builder
* @var Database_Query_Builder_Where
*/
protected $_db_builder;

//...................................................

/**
* Initializes the Database Builder to given query type
*
* @param integer $type Type of Database query
* @return ORM
*/
protected function _build($type)
{
// Construct new builder object based on query type
switch ($type)
{
case Database::SELECT:
$this->_db_builder = DB::select();
break;
case Database::UPDATE:
$this->_db_builder = DB::update(array($this->_table_name, $this->_object_name));
break;
case Database::DELETE:
$this->_db_builder = DB::delete(array($this->_table_name, $this->_object_name));
}

// Process pending database method calls
foreach ($this->_db_pending as $method)
{
$name = $method['name'];
$args = $method['args'];

$this->_db_applied[$name] = $name;

call_user_func_array(array($this->_db_builder, $name), $args);
}

return $this;
}


//...................................................

/**
* Finds multiple database rows and returns an iterator of the rows found.
*
* @return Database_Result
*/
public function find_all()
{
if ($this->_loaded)
throw new Kohana_Exception('Method find_all() cannot be called on loaded objects');

if ( ! empty($this->_load_with))
{
foreach ($this->_load_with as $alias)
{
// Bind auto relationships
$this->with($alias);
}
}

$this->_build(Database::SELECT);

return $this->_load_result(TRUE);
}

//...................................................

} // End ORM

Na most a többi érintett kódrészletet már nem másolgatom be, mert akkor kicsit hosszú lenne a hozzászólásom, de ami ebből is látható (meg a többi kódrészletből), az alátámasztani látszik azt, amit modder írt.

===

(#5) : Ezek szerint addig nem jelenik meg a buborék, amíg meg nem jelenik az első hsz.? Hmm.
A példára rátérve igazából arra lettem volna kíváncsi, hogyan tesztelted, ami miatt a sima Iterator interface implementációjával történő tesztelés elszállt futási idő túllépésével. Meglepő, ha ekkora a különbség, ilyen esetben meg azért nem árt tudni, mi volt a tesztkörnyezet, amiben ez teljesült.

Sk8erPeter

(#8) Peter Kiss válasza Sk8erPeter (#7) üzenetére


Peter Kiss
senior tag
LOGOUT blog

Ebből semmi sem látszik, mert ez csak query builder + végrehajtás, de az eredmény már nincs benne. modder azt írta, hogy folyamatosan fetchel, ebben ez nincs benne, és én ezt kétlem. Azt, hogy iterálható az adathalmaz, az nem vita tárgya, az, ha működik.

---

Nem, addig nem jelenik meg.

Core i5 @3GHz 1 magon 128 vagy 256 MB memóriát adva a PHP-nak.

(#9) Sk8erPeter válasza Peter Kiss (#8) üzenetére


Sk8erPeter
nagyúr

"mi volt a tesztkörnyezet"
Itt eléggé pontatlan volt, amit írtam, mert nem csak a konfiguráció érdekelt volna, hanem a konkrét kód.

A Kohanás kódra:
igazából abbahagytam a bemásolást, és nem raktam be azt, amit még kellett volna: a _load_result() függvényt:

..............
/**
* Loads a database result, either as a new record for this model, or as
* an iterator for multiple rows.
*
* @chainable
* @param bool $multiple Return an iterator or load a single row
* @return ORM|Database_Result
*/
protected function _load_result($multiple = FALSE)
{
$this->_db_builder->from(array($this->_table_name, $this->_object_name));

if ($multiple === FALSE)
{
// Only fetch 1 record
$this->_db_builder->limit(1);
}

// Select all columns by default
$this->_db_builder->select($this->_object_name.'.*');

if ( ! isset($this->_db_applied['order_by']) AND ! empty($this->_sorting))
{
foreach ($this->_sorting as $column => $direction)
{
if (strpos($column, '.') === FALSE)
{
// Sorting column for use in JOINs
$column = $this->_object_name.'.'.$column;
}

$this->_db_builder->order_by($column, $direction);
}
}

if ($multiple === TRUE)
{
// Return database iterator casting to this object type
$result = $this->_db_builder->as_object(get_class($this))->execute($this->_db);

$this->reset();

return $result;
}
else
{
// Load the result as an associative array
$result = $this->_db_builder->as_assoc()->execute($this->_db);

$this->reset();

if ($result->count() === 1)
{
// Load object values
$this->_load_values($result->current());
}
else
{
// Clear the object, nothing was found
$this->clear();
}

return $this;
}
}
.....

Sk8erPeter

(#10) Peter Kiss válasza Sk8erPeter (#9) üzenetére


Peter Kiss
senior tag
LOGOUT blog

Melóban vagyok, a kód nincs nálam.

---

Az plusz kód sem árul el semmit sem arról, hogyan fetcheli az obejktumokat. Egyébként szerintem nagyon f.s érdekes ez a kód. :DDD

További hozzászólások megtekintése...
Copyright © 2000-2024 PROHARDVER Informatikai Kft.