Статьи

Преобразователи в PHP Made Easy

Вы когда-нибудь слышали о функциональном программировании , функциях высокого порядка и т. Д.? Наверное, верно? Однако когда вы слышите «преобразователи», знаете ли вы, что это такое?

Векторная иллюстрация ввода / вывода

Определение преобразователей

Мы не можем определить преобразователи, не поговорив сначала о преобразователях. Цитируя Рич Хики :

Редукционная функция — это просто та функция, которую вы должны передать, чтобы уменьшить — она ​​требует результата и нового ввода и возвращает следующий результат.

Преобразователь — это функция, которая принимает одну сокращающую функцию и возвращает другую.

Преобразователи были впервые введены в Clojure Ричем Хики, а портированы на PHP Майклом Даулингом . Преобразователи являются мощным способом построения алгоритмических преобразований, которые вы можете использовать во многих контекстах. В этой статье мы рассмотрим, как они могут быть полезны, на ряде практических примеров.

Примеры

Нам нужно установить пакет Transducers через Composer, прежде чем идти дальше.

composer require mtdowling / transducers 

Мы будем использовать простой класс User для следующих примеров.

 class User { public $id ; public $name ; public $age ; public function __construct ( $id , $name , $age ) { $this - > id = $id ; $this - > name = $name ; $this - > age = $age ; } public function __toString ( ) { return sprintf ( "\n%d - %s - %d" , $this - > id , $this - > name , $this - > age ) ; } } // demo data $data = [ new User ( 1 , "younes" , 24 ) , new User ( 2 , "youssef" , 26 ) , new User ( 3 , "hamza" , 25 ) , new User ( 4 , "ismail" , 17 ) , ] ; 
 use Transducers as t ; $uppercase = t\ map ( function ( $user ) { return new User ( $user - > id , ucfirst ( $user - > name ) , $user - > age ) ; } ) ; $result = t\ xform ( $data , $uppercase ) ; var_dump ( $result ) ; 

Функция map аналогична PHP-функции array_map : мы передаем вызываемый объект, который, в данном случае, будет вводить первую букву имени пользователя в верхнем регистре.

Мы используем функцию xform для применения нашего преобразователя в uppercase . Он принимает наши данные для первого параметра и преобразователь для второго.

 // output array ( 4 ) { [ 0 ] = > object ( User ) #14 (3) { [ "id" ] = > int ( 1 ) [ "name" ] = > string ( 6 ) "Younes" [ "age" ] = > int ( 24 ) } [ 1 ] = > object ( User ) #15 (3) { [ "id" ] = > int ( 2 ) [ "name" ] = > string ( 7 ) "Youssef" [ "age" ] = > int ( 26 ) } [ 2 ] = > object ( User ) #16 (3) { [ "id" ] = > int ( 3 ) [ "name" ] = > string ( 5 ) "Hamza" [ "age" ] = > int ( 25 ) } [ 3 ] = > object ( User ) #17 (3) { [ "id" ] = > int ( 4 ) [ "name" ] = > string ( 6 ) "Ismail" [ "age" ] = > int ( 17 ) } } 

xform возвращает тот же тип, что и параметр data (в данном случае массив). Мы также можем использовать to_array если вы строго хотите вывести массив.

 // ... $result = t\ to_array ( $data , $uppercase ) ; // ... 

Мы также можем использовать to_string для преобразования вывода в строку или into($target, $coll, callable $xf) для преобразования вывода в определенный тип. Проверьте документацию для более подробной информации.

 use Transducers as t ; $uppercase = t\ map ( function ( $user ) { return new User ( $user - > id , ucfirst ( $user - > name ) , $user - > age ) ; } ) ; $result = t\ to_string ( $data , $uppercase ) ; var_dump ( $result ) ; 
 // output string ( 64 ) " 1 - Younes - 24 2 - Youssef - 26 3 - Hamza - 25 4 - Ismail - 17 " 

Самое приятное в преобразователях — это то, что мы можем объединить несколько преобразований в один преобразователь. Например, давайте начнем с прописной буквы первой буквы имени пользователя и удалим несовершеннолетних.

 $uppercase = t\ map ( function ( $user ) { return new User ( $user - > id , ucfirst ( $user - > name ) , $user - > age ) ; } ) ; $removeMinors = t\ filter ( function ( $user ) { return $user - > age >= 18 ; } ) ; $comp = t\ comp ( $uppercase , $removeMinors ) ; $result = t\ to_string ( $data , $comp ) ; var_dump ( $result ) ; 

Функция filter аналогична PHP-функции array_filter . Функция comp создает преобразователь из списка преобразователей, в данном случае в uppercase (с использованием map ) и removeMinors (с использованием filter ).

 // output string(48) " 1 - Younes - 24 2 - Youssef - 26 3 - Hamza - 25" 

Теперь у нас есть состав преобразователя многоразового использования, который мы можем использовать всякий раз, когда мы хотим уменьшить наши данные, используя этот критерий. Проверьте документацию для списка доступных функций сокращения.

Создание преобразователя

Редукционная функция принимает значение в качестве параметра и возвращает массив редукционной функции, который должен содержать три элемента:

  • init : функция, которая возвращает начальное значение для преобразователя. Вызывается только сначала, если не указано начальное значение.
  • result : функция result вызывается для создания окончательного результата из стека вызовов.
  • step : здесь вы пишете свою логику редукции — вы можете назвать ее нулем или много раз в зависимости от вашей логики редуктора.

Это действительно сбивает с толку, не показывая некоторый реальный код, так что давайте использовать функцию take transducer в качестве примера. Он берет n элементов сверху массива данных.

 // .... $comp = t\ comp ( $uppercase , $removeMinors , t\ take ( 2 ) ) ; $result = t\ to_string ( $data , $comp ) ; var_dump ( $result ) ; 
 // output string(33) " 1 - Younes - 24 2 - Youssef - 26" 

Вот исходный код функции Take Reducer.

 function take ( $n ) { return function ( array $xf ) use ( $n ) { $remaining = $n ; return [ 'init' = > $xf [ 'init' ] , 'result' = > $xf [ 'result' ] , 'step' = > function ( $r , $input ) use ( & $remaining , $xf ) { $r = $xf [ 'step' ] ( $r , $input ) ; return -- $remaining > 0 ? $r : ensure_reduced ( $r ) ; } ] ; } ; } 

Функция take вызывается несколько раз с result и input параметрами. При каждом вызове он уменьшает remaining переменную и проверяет, не меньше ли она нуля. В этом случае мы возвращаем экземпляр объекта Reduced , который указывает точку остановки.

Наш пример функции преобразователя удалит из данных нулевые элементы. Используя предыдущее объяснение того, как работают преобразователи, мы можем получить доступ к переменной $input и решить, вызывать ли обратный вызов следующего step или просто вернуть значение.

 function dropNull ( ) { return function ( array $xf ) { return [ 'init' = > $xf [ 'init' ] , 'result' = > $xf [ 'result' ] , 'step' = > function ( $result , $input ) use ( $xf ) { return $input === null ? $result : $xf [ 'step' ] ( $result , $input ) ; } ] ; } ; } 

Мы можем проверить это, добавив некоторые null элементы в нашу переменную $data .

 $data = [ null , new User ( 1 , "younes" , 24 ) , new User ( 2 , "youssef" , 26 ) , new User ( 3 , "hamza" , 25 ) , new User ( 4 , "ismail" , 17 ) , null ] ; $result = t\ to_string ( $data , t\ dropNull ( ) ) ; var_dump ( $result ) ; 
 // output string(64) " 1 - younes - 24 2 - youssef - 26 3 - hamza - 25 4 - ismail - 17" 

Вывод

В этой статье мы познакомились с новым аспектом мира функционального программирования, называемым преобразователями. Мы перешли к цели преобразователей, которая заключается в упрощении преобразования данных. Мы также рассмотрели несколько примеров, чтобы лучше продемонстрировать ценность преобразователей. Теперь у вас есть новый инструмент в поясе разработчика или, по крайней мере, лучшее понимание концепции преобразователя.

Если у вас есть какие-либо вопросы о преобразователях, вы можете опубликовать их ниже!