Несколько недель назад в списке рассылки PHP было интересное предложение с чьим-то хаком, где описаны генераторы, выражения-генераторы и выделения списков. Неудивительно, что некоторые люди не уверены в перспективах этого предложения, как и core разработчики, так и пользователи PHP. Я хотел бы поделиться несколькими идеями и попытаться убедить этих парней.
Самый быстрый пример, который я смог придумать, это простой интерфейс для парсинга больших XML файлов.
И возможный вариант парсинга с использованием этих функций.
Я уверен, что те, кто уже использовал
Это не общий паттерн, но когда мне приходится общаться со внешними сервисами мне нравится оборачивать их ответ в две части:
Как я говорил, я не видел похожее в чужом коде, но для меня это частый паттерн, который с использованием генераторов стал проще.
Ленивые вычисления, основополагающий принцип генераторов, малоиспользуемый стиль программирования у большинства разработчиков на PHP, и я бы хотел увидеть как люди начнут вникать в него, если эта возможность попадет в PHP.
Не сложно представить себе людей использующих смешанные стили (ООП и процедурное программирование). Они могли бы начать с чего-то простого, например:
А может быть даже построить что-то вроде LINQ? Не то, что уже существует.
Там было несколько комментариев в которых люди заметили этот аспект и были против. Я не понимал тогда и не понимаю сейчас, почему это плохо. Я, например, считаю это хорошей новостью: ни один язык не является совершенным и каждый из них должен стараться включать особенности, которые оказались полезными в других (конечно, с учетом возможности реализации).
Если вы дочитали статью до этих строчек, и не потеряли интерес, я рекомендую перейти в терминал и выполнить следующие команды до того, как вы закроете вкладку.
Декларативный разбор XML
Самый быстрый пример, который я смог придумать, это простой интерфейс для парсинга больших XML файлов.
<?php
function *readXML($file) {
$r = new XMLReader;
$r->open($file);
while($r->read()) {
yield $r;
}
}
function *filterNodes(callable $predicate, $nodes) {
foreach($nodes as $node) if($predicate($node)) yield $node;
}
function matchName($name) {
return function($node) use($name) {
return $node->name === $name;
};
}
И возможный вариант парсинга с использованием этих функций.
<?php
function *getArticlesFromXML($file) {
foreach(filterNodes(matchName('article'), readXML($file)) as $node)
yield nodeToArticle($node);
}
function nodeToArticle($node) {
// transform node
return $node->name;
}
$articles = [foreach(getArticlesFromXML('data.xml') as $article) yield $article];
Я уверен, что те, кто уже использовал
XMLReader
, поймут, как контроль за парсингом был взят из цикла while
, и «скорректирован» в декларативный стиль с помощью функций высшего порядка.Простая декорация итераторов
Это не общий паттерн, но когда мне приходится общаться со внешними сервисами мне нравится оборачивать их ответ в две части:
- статусная информация — ok, failure, retry-action, get-from-cache и т.д.
- результаты — обернутые в итератор, который декорирует их во время доступа.
Как я говорил, я не видел похожее в чужом коде, но для меня это частый паттерн, который с использованием генераторов стал проще.
<?php
// inside some class
public function getResult() {
return SomeCustomIterator($this->results, new SomeCustomDecorator);
}
// with generators
public function *getResults() {
foreach($this->results as $result)
yield new SomeCustomDecorator($result);
}
Снова о более выразительном API
Ленивые вычисления, основополагающий принцип генераторов, малоиспользуемый стиль программирования у большинства разработчиков на PHP, и я бы хотел увидеть как люди начнут вникать в него, если эта возможность попадет в PHP.
Не сложно представить себе людей использующих смешанные стили (ООП и процедурное программирование). Они могли бы начать с чего-то простого, например:
<?php
function select() {
return func_get_args();
}
function from($source) {
return $source;
}
function where() {
return func_get_args();
}
function *query($select, $from, $where) {
foreach($from as $element) {
foreach($where as $predicate) {
if(false === $predicate($element))
continue 2;
}
$fields = array();
foreach($select as $selector) {
$fields[] = $selector($element);
}
yield $fields;
}
}
$articles = query(
select(property('name')),
from(readXML('data.xml')),
where(matchName('article'))
);
// where property could be something as simple as
function property($name) {
return function($node) use ($name) {
return $node->{$name};
};
}
А может быть даже построить что-то вроде LINQ? Не то, что уже существует.
Выглядит как Python
Там было несколько комментариев в которых люди заметили этот аспект и были против. Я не понимал тогда и не понимаю сейчас, почему это плохо. Я, например, считаю это хорошей новостью: ни один язык не является совершенным и каждый из них должен стараться включать особенности, которые оказались полезными в других (конечно, с учетом возможности реализации).
Протестируйте сами
Если вы дочитали статью до этих строчек, и не потеряли интерес, я рекомендую перейти в терминал и выполнить следующие команды до того, как вы закроете вкладку.
$ git clone -b addListComprehensions https://github.com/nikic/php-src.git
$ cd php-src
$ ./buildconf
$ ./configure
$ make cli
$ ./sapi/cli/php some-example-file.php