Рекурсия или не рекурсия? Circular select или circular array?

Доброе время суток. Возникла задача реализовать функцию, которая возвращает данные из массива (в идеале это база данных), таким образом, что элементов можно получить N, независимо от размера этих данных. В итоке получается примерно такая структура:

200px-Circular_buffer.svg.png

По-подробности:
имеем
[$model1, $model2, $model3, $model4, $model5, $model6]
. Вызываем нашу функцию таким способом:
$repo = [$model1, $model2, $model3, $model4, $model5, $model6];
$startModel = $repo[3]; //любая рандомная модель, берем $model4
$direction = -1;
$n = 4;
$result = $startModel->seek($direction, $n);


Что здесь есть. Есть модели (не важно что за модели, просто модели, у которых есть метод, позволяющий получить $n элементов в сторону $direction (-1 - лево, 1 - право). И этот метод должен вернуть:
[$model6, $model1, $model2, $model3]

То есть, простыми словами это может выглядеть так:
seek([1, 2, 3, 4, 5, 6], 3, -1, 4)
Результат: [5, 6, 1, 2]
1й параметр - наши данные, второй - с какого элемента двигаться, третий - направление сдвига, 4й - количество необходимых элементов

Количество элементов может быть даже большим чем всего элементов в базе, например:
seek([1, 2, 3, 4], 3, -1, 10);

Результат: [1, 2, 3, 4, 1, 2, 3, 4, 1, 2]

Сначала мне пришло в голову сделать такое рекурсивно, и набросал такой код (извиняюсь за его размер):

<?php

class Model {
    protected $id;

    public function __construct($id) {
        $this->id = $id;
    }

    public function getId() {
        return $this->id;
    }

    public function seek($direction = 1, $count = 5, $context = null) {
        global $repo;
        $result = array();
        is_null($context) and $context = $this;

        if ($direction < 0) {
            $leftCount = $context->getId() - $count;
            $leftCount < 0 and $leftCount = 0;

            for($i = $context->getId(); $i > $leftCount; $i--) {
                $result[] = $repo[$i];
            }

            if (count($result) < $count) {
                $newContext = $repo[count($repo) - 1];
                $newCount = $count - count($result);
                $result = array_merge($result, $newContext->seek($direction, $newCount));
            }
        } else {
            $rightCount = $count + $context->getId();
            $rightCount > count($repo) and $rightCount = count($repo);

            for($i = $context->getId(); $i <= $rightCount; ++$i) {
                $result[] = $repo[$i];
            }

            if (count($result) < $count) {
                $newContext = $repo[0];
                $newCount = $count - count($result);
                $result = array_merge($result, $newContext->seek($direction, $newCount));
            }
        }

        return $result;
    }
}

$ids = range(1, 6);
$repo = array();

foreach($ids as $id) {
    $repo[] = new Model($id);
}

/**
 * Random model
 */
$startModel = $repo[3];

//var_dump($startModel->getId());
$result = $startModel->seek(-1, 10);

foreach($result as $model) {
    var_dump($model->getId());
}


В принципе он работает (есть правда баг, который я не могу впоймать, но суть не в этом)

В чем заключается мой вопрос: то что я сделал - работает с уже загруженным масивом данных, а если нельзя загрузить сразу все данные ? Вопрос очень размазанный, я понимаю, но это еще не всё. Мой подход - рекурсия. Я подумал о том, что можно на стороне базы данных попытаться сделать такое. Т.е. например генерировать sql с UNION ALL на стороне php, и потом получать все нужные данные одним запросом..

Если непонятно описал, пните пожалуйста, просто очень специфичная задача какая то, не нашел готовых решений, а своё пока что делает не совсем то что хотелось бы.
  • Вопрос задан
  • 2578 просмотров
Решения вопроса 1
egor_nullptr
@egor_nullptr
В сторону InfiniteIterator смотрели?
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы