За последние 24 часа нас посетили 14873 программиста и 1728 роботов. Сейчас ищут 690 программистов ...

Агрегатор итераторов

Тема в разделе "Решения, алгоритмы", создана пользователем Ti, 2 фев 2009.

  1. Ti

    Ti Активный пользователь

    С нами с:
    3 июл 2006
    Сообщения:
    2.378
    Симпатии:
    1
    Адрес:
    d1.ru, Екатеринбург
    Что это за зверь:
    PHP:
    1. <?
    2. $list = new MultiList;
    3. $list->add(new ArrayObject(array(1,4,10)));
    4. $list->add(new ArrayObject(array(3,6,7)));
    5. $list->add(new ArrayObject(array(2,5,7)));
    6. $list->add(new ArrayObject(array(0)));
    7.  
    8. foreach($list as $item) echo $item."\n";
    9.  
    можно управлять сортировкой объявляя свой метод сортировки (по-умолчанию сортировка по возрастанию)
    важно что бы используемые итераторы были уже отсортировани в том же порядке
    PHP:
    1. <?
    2. function myCmpFunction($a, $b) {
    3.     if ($a == $b) return 0;
    4.     return $a < $b ? 1 : -1;
    5. }
    6. $list->setCmpFunction('myCmpFunction');
    7.  
    или используя готовый метод сортировки по атрибуту (если итераторы возращают объект)
    PHP:
    1. <?
    2. $list->sortByAttribute('timeAdd');
    3.  

    Собственно сам класс
    PHP:
    1. <?
    2. /**
    3.  * Класс агрегирует несколько итераторов
    4.  */
    5. class MultiList implements Iterator, Countable {
    6.     private $collect = array();
    7.     private $cmpFunction;
    8.     private $key;
    9.     private $currentList;
    10.  
    11.  
    12.     /**
    13.      * @param Traversable $list
    14.      * @param Traversable $list
    15.      * ...
    16.      */
    17.     function __construct() {
    18.         foreach (func_get_args() as $list) $this->add($list);
    19.     }
    20.  
    21.  
    22.     /**
    23.      * @param Traversable $list
    24.      *
    25.      * @return MultiList
    26.      */
    27.     function add(Traversable $list) {
    28.         if ($list instanceof IteratorAggregate) {
    29.             $list = $list->getIterator();
    30.         }
    31.         if ($list instanceof Countable) {
    32.             $this->collect[] = $list;
    33.         }
    34.         else {
    35.             throw new Exception('Object not instanceof Countable');
    36.         }
    37.         return $this;
    38.     }
    39.  
    40.  
    41.     /**
    42.      * Использовать специфичную функцию для сортировки
    43.      *
    44.      * @param mixed $cmpFunction
    45.      * @return MultiList
    46.      */
    47.     function setCmpFunction($cmpFunction) {
    48.         if (is_callable($cmpFunction)) {
    49.             $this->cmpFunction = $cmpFunction;
    50.         }
    51.         else {
    52.             throw new Exception('$cmpFunction is not callable');
    53.         }
    54.         return $this;
    55.     }
    56.  
    57.  
    58.     private $cmpAttribute;
    59.     private $cmpAsc;
    60.  
    61.  
    62.     /**
    63.      * Использовать для сортировки атрибут объекта
    64.      *
    65.      * @param string $attribute
    66.      * @param bool $asc = true
    67.      */
    68.     function sortByAttribute($attribute, $asc = true) {
    69.         $this->cmpAttribute = $attribute;
    70.         $this->cmpAsc = $asc;
    71.         $this->setCmpFunction(array($this, 'cmpAttributeCallback'));
    72.     }
    73.  
    74.  
    75.     function cmpAttributeCallback($a, $b) {
    76.         $attribute = $this->cmpAttribute;
    77.         $a = $a->$attribute;
    78.         $b = $b->$attribute;
    79.         if ($a == $b) return 0;
    80.         $result = $a < $b ? -1 : 1;
    81.         return $this->cmpAsc ? $result : $result * -1;
    82.     }
    83.  
    84.  
    85.     function rewind() {
    86.         foreach ($this->collect as $list) $list->rewind();
    87.         $this->key = 0;
    88.         $this->currentList = null;
    89.         return $this->current();
    90.     }
    91.  
    92.  
    93.     function current() {
    94.         if ($this->valid()) {
    95.             return $this->getCurrentList()->current();
    96.         }
    97.         else {
    98.             return null;
    99.         }
    100.     }
    101.  
    102.  
    103.     private function getCurrentList() {
    104.         if (is_null($this->currentList)) {
    105.             $values = array();
    106.             foreach ($this->collect as $key=>$list) {
    107.                 if ($list->valid()) $values[$key] = $list->current();
    108.             }
    109.             if ($values) {
    110.                 if (is_null($this->cmpFunction)) {
    111.                     asort($values);
    112.                 }
    113.                 else {
    114.                     uasort($values, $this->cmpFunction);
    115.                 }
    116.                 reset($values);
    117.                 $key = key($values);
    118.                 $this->currentList = $this->collect[$key];
    119.             }
    120.             else {
    121.                 $this->currentList = false;
    122.             }
    123.         }
    124.         return $this->currentList;
    125.     }
    126.  
    127.  
    128.     function next() {
    129.         if ($this->valid()) {
    130.             $this->getCurrentList()->next();
    131.             $this->key++;
    132.         }
    133.         $this->currentList = null;
    134.         return $this->current();
    135.     }
    136.  
    137.  
    138.     function valid() {
    139.         return (bool) $this->getCurrentList();
    140.     }
    141.  
    142.  
    143.     function key() {
    144.         return $this->key;
    145.     }
    146.  
    147.  
    148.     function count() {
    149.         $sum = 0;
    150.         foreach ($this->collect as $list) $sum += $list->count();
    151.         return $sum;
    152.     }
    153. }
     
  2. armadillo

    armadillo Активный пользователь

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    АгрЕгатор
     
  3. Ti

    Ti Активный пользователь

    С нами с:
    3 июл 2006
    Сообщения:
    2.378
    Симпатии:
    1
    Адрес:
    d1.ru, Екатеринбург
    Класс был написан для вывода новостей с разных таблиц где каждый итератор по сути SQL SELECT

    fix)
     
  4. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.557
    Симпатии:
    631
    Норм, удобное решение, единственное что смущает это вызов функции сортировки при каждом получении очередного элемента. Сколько элементов - столько раз сортировка, годится только для небольших списков. Но концепция хорошая.