Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Портируем C# LINQ на PHP

Anna | 31.05.2014 | нет комментариев

Функциональность LINQ запросов в C# переписана на PHP. Первоначально библиотека задумывалась как тренинг, так как сходственные библиотеки теснее существуют, но потом было решено её опубликовать для всех. Скопировать LINQ на PHP один в один не допустимо, от того что вероятности языков C# и PHP дюже различные. Отличительной вероятностью предлагаемого решения является ориентация на итераторы, ленивые лямбда выражения и сигнатура LINQ способов, одинаковая C#. Все типовые LINQ способы, безусловно, реализованы. Изложение вероятностей плана с объяснением причин, отчего именно такое решение было выбрано, под катом.

image

Отчего LINQ?

Новые спецтехнологии это неизменно увлекательно. Отчего они появились, какие задачи решают, как решают? Одной из таких фишек является LINQ (Language Integrated Query), SQL сходственный язык запросов к последовательностям данных (массивы, результаты баз данных, коллекции). Скажем,

var q = from c in db.Customers
        where c.Activity == 1
        select new { c.CompanyName, c.ItemID, c.ItemName };

В C# помощь такого синтаксиса встроена на ярусе языка, правда на самом деле это синтаксический сахар, тот, что преобразуется к дальнейшему виду

var q = db.Customers.
        Where((c) => c.Activity == 1).
        Select((c) => { c.CompanyName, c.ItemID, c.ItemName });

Тут функции Where и Select определены над последовательностями данных, но логика обработки задана для отдельного элемента. Сделано в духе функционального программирования — все есть функция и итог вычисления функции зависит только от входных параметров. Требование к чистоте функций разрешает априори исключить ошибки из-за побочных результатов. Есть и другие плюсы:

  • Так как порядок обработки не значим, то обработку над большинством дозволено делать параллельно.
  • Взамен локального перебора LINQ запросы могут сворачиваться (в т.ч. Отчасти) в обыкновенные SQL запросы к базам данных. Запрос наружно остается тем же.
  • Логика обработки отдельного элемента изолирована. Её дозволено вторично применять для других коллекций либо комбинировать с другими.

Было бы отлично иметь такой же инструмент в PHP. Было бы отлично, как и в C#, без специальных дополнительных вычислительных расходов. И правда такие библиотеки есть, всякий реализует эту функциональность по иному. Повод в том, что комфортная в применении реализация LINQ тянет за собой многие вероятности C#, которых в PHP нет в надобном виде и их нужно иммитировать. А здесь вкусы у всех различные.

Отчего невозможно скопировать?

Перечислим каких вероятностей C# на 1-й взор не хватает для копирования библиотеки в PHP.

  • нестрогая типизация. Это скорее превосходство.
  • нет перегрузки способов. Это следствие из предыдущего пункта. К примеру, если вам нужно сравнить строки, массивы и объекты, то нужно писать три различные функции с именами cmpStringcmpArray,cmpLaptop либо ставить if внутри одной огромный. Оба решения дрянны. В первом случае, информация о типе в имени «загрязняет» код, где эти функции применяются. Во втором случае, трудно расширять функционал.
  • нет растяжений классов. Вы не можете написать способ и вызывать его как словно это способ иного класса, то есть невозможно расширять IEnumerable<T>, легко подключая свой namespace. Но в PHP есть волшебный способ __call, через тот, что дозволено вызывать статические способы, реализованные в ином месте. Правда нужно модифицировать желаемый класс, а это не неизменно допустимо. Так же стоит позабыть про поддержку в IDE.
  • нет генераторов с прекрасным return yield (php<5.5). С одной стороны это синтаксический сахар. Дозволено написать функцию, возвращающую \Iterator, тот, что по next() будет вызывать неизвестную функцию, которая будет вычислять значение дальнейшего элемента и сберегать свое состояние в признаках класса. Но размер кода увеличится неоднократно, а его пригодная доля упадет. В версии 5.5 возникли генераторы, но нужно еще длинно ожидать, пока эта версия станет знаменитой.
  • нет лямбда выражений. Это такие маленькие неизвестные функции, функционал которых лимитирован, но при этом сохраняется информация о структуре. Её дозволено испоИнформация о структуре выражения при этом не теряется и может быть в последствии использована вторично. Основой служит класс ExpressionBuilder, тот, что в потоковом режиме создает дерево вычисления и экспортирует его в обратную польскую (постфиксная) запись. Скажем, так
    $exp = new ExpressionBuilder();
    $exp->add(1);
    $exp->add(' ',1);
    $exp->add(2);    
    $exp->export(); // [1, 2, 2, ' ']
    

    Поддерживаются приоритеты операций и скобки. Класс Expression пробегает по выгруженному массиву и, если встречает данные, то закидывает их в стек, а если встречает объект типа OperationInterface, то передает управление ему. Объект достает надобное число доводов из стека, вычисляет итог и закидывает его обратно в стек. По окончанию в стеке остается одно значение — итог. На больше высоком ярусе выражения строятся с поддержкой класса LambdaInstance и его декоратор Lambda. Примеры вероятностей.

    1. доступ к доводам, константы
      /* одинаковые функции */
      $f = Lambda::v();
      $f = function ($x) { return $x; };
      
    2. математические операции, операции сопоставления, логические операции
      $f = Lambda::v()->add()->v()->mult(12)->gt(36);
      $f = function ($x) { return $x   $x*12 > 36; };
      
    3. скобки
      $f = Lambda::v()->mult()->begin()->c(1)->add()->v()->end();
      $f = function ($x) { return $x * (1   $x); };
      
    4. строковые операции
      $f = Lambda::v()->like('hallo%');
      
    5. генерация массива
      $f = Lambda::complex([ 'a'=>1, 'b'=>Lambda::v() ]);
      $f = function ($x) { return [ 'a' => 1, 'b' => $x ]; };
      
    6. доступ к свойствам и способам объекта, элементам массива
      $f = Lambda::v()->items('car');
      $f = Lambda::v()->getCar();
      $f = Lambda::car;
      $f = function ($x) { return $x->getCar(); };
      
    7. вызов глобальных функций
      $f = Lambda::substr(Lambda::v(), 3, 1)->eq('a');
      $f = function ($x) { return substr($x,3,1) == 'a'; };
      

    Безусловно, при вычислении лямбда выражения производятся добавочно побочные операции. Для фукнкции (x)=>x 1 скорость вычисления Lambda в 15 раз неторопливей прямого вызова функции, а сама конструкция требует для хранения в 3600 байт памяти вопреки 800. Планируется провести обзор профайлером, Дабы разобраться как увеличить скорость и уменьшить память.

    Встречают по интерфейсу, а провожают по реализации

    Все LINQ способы взяты из стандартного .NET 4.5 и раскиданы по соответствующим интерфейсам (GenerationInterfaceFilteringInterface, etc.) с изложением из MSDN. Получилось много файлов, но добавочная нагрузка на разбор файлов не должна быть огромный, исключительно, если включено кэширование. Сигнатура способов осталась насколько это допустимо неизмененной с учетом вероятностей PHP. Интерфейс IEnumerable наследует все упомянутые интерфейсы и\IteratorAggregate. Класс Linq реализует интерфейсы IEnumerable для локального перебора. В последующем дозволено сделать иную реализацию IEnumerable, которая будет собирать SQL запрос либо будет фасадом к Doctrine. Реализованые следующие способы.

    • Aggregation — aggregate, average, min, max, sum, count
    • Concatenation — concat, zip
    • Element — elementAt, elementAtOrDefault, first, firstOrDefault, last, lastOrDefault, single, singleOrDefault
    • Equality — isEqual
    • Filtering — ofType, where
    • Generation — from, range, repeat
    • Grouping — groupBy
    • Joining — product, join, joinOuter, groupJoin
    • Partitioning — skip, skipWhile, take, takeWhile
    • Projection — select, selectMany, cast
    • Quantifier — all, any, contains
    • Set — distinct, intersect, except, union
    • Sorting — orderBy, orderByDescending, thenBy, thenByDescending, reverse, order
    • Другое — toArray, toList, each

    Если в способе нужно указать источник данных, то это может быть массив (array), функция (callable) либо итератор (\Iterator\IteratorAggregate). Подобно в качестве выражения может быть передана строка (string), функция (callable), массив (array) либо лямбда выражение (\LambdaInterface). Ниже несколько примеров, есть так же многообразные юнит-тесты.

    // Grouping Sorting Filtering array expression
    $x = Linq::from($cars)->group(Lambda::v()->getCategory()->getId())->select([
        'category' => Lambda::i()->keys(),
        'best'     => Lambda::v()->linq()
            ->where(Lambda::v()->isActive()->eq(true))
            ->orderBy(Lambda::v()->getPrice())
            ->last()
    ])
    // Set   LambdaInterface expression
    $x = Linq::from($cars)->distinct(Lambda::v()->getCategory()->getTitle());
    // Set   string expression
    $x = Linq::from($cars)->distinct('category.title');
    // Generation callable
    $fibonacci = function () {
        $position = 0;
        $f2 = 0;
        $f1 = 1;
        return function () use (&$position, &$f2, &$f1) {
             $position  ;
             if ($position == 1) {
                 return $f2;
             } elseif ($position == 2) {    
                 return $f1;
             } else {
                 $f = $f1   $f2;
                 $f2 = $f1;
                 $f1 = $f;
                 return $f;
             }
        }
    }
    $x = Linq::from($fibonacci)->take(10);
    

    Функция, которая вернула функцию, которая вернула функцию, которая …

    Всякий LINQ способ создает объект класса Linq, которому передается инициализирующая неизвестная функция и ссылка на другие Linq объекты, итераторы которых являются входными данными для инициализирующей функции. Так как Linq реализует интерфейс \IteratorAggregate, то при запросе первого элемента итераторы механически инициализируются по цепочке вверх.

    Для чего все это?

    Спасибо каждому, кто дочитал до конца. План делался для тренировки мозгов и рук, следственно любая обстоятельная критика приветствуется на 200%. Мне дюже хотелось поделиться работой, которой в всеобщем доволен. Если он кому-либо еще и реально сгодится, то вообще восхитительно.

    Каждый код документирован, аннотирован, покрыт тестами и опубликован на github под лицензией BSD (modified). Это всецело рабочая библиотека.

Источник: programmingmaster.ru

Оставить комментарий
БАЗА ЗНАНИЙ
СЛУЧАЙНАЯ СТАТЬЯ
СЛУЧАЙНЫЙ БЛОГ
СЛУЧАЙНЫЙ МОД
СЛУЧАЙНЫЙ СКИН
НОВЫЕ МОДЫ
НОВЫЕ СКИНЫ
НАКОПЛЕННЫЙ ОПЫТ
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB