A Functional Guide to Cat Herding with PHP Generators The Filter/Map/Reduce Pattern for PHP Generators
A Functional Guide to Cat Herding with PHP Generators • Blog Post http://markbakeruk.net/2016/01/19/a-functional-guide-to-cat-herding-with- php-generators/ • Code Examples https://github.com/MarkBaker/GeneratorFunctionExamples
A Functional Guide to Cat Herding with PHP Generators
A Functional Guide to Cat Herding with PHP Generators
A Functional Guide to Cat Herding with PHP Generators <?xml version="1.0" encoding="ISO-8859-1"?> <gpx version="1.1" creator="Memory-Map 5.4.2.1089 http://www.memory-map.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"> <trk> <name>Wythburn</name> <type>Track</type> <trkseg> <trkpt lat="54.5131924947" lon="-3.0448236664"><time>2015-03-02T07:59:35Z</time></trkpt> <trkpt lat="54.5131921768" lon="-3.0450893323"><time>2015-03-02T08:00:31Z</time></trkpt> <trkpt lat="54.5131534894" lon="-3.0448548317"><ele>192</ele><time>2015-03- 02T08:00:51Z</time></trkpt> ... <trkpt lat="54.4399968465" lon="-2.9721705119"><ele>52</ele><time>2015-03- 02T14:50:49Z</time></trkpt> </trkseg> </trk> </gpx>
A Functional Guide to Cat Herding with PHP Generators namespace GpxReader; class GpxHandler { protected $gpxReader; public function __construct($gpxFilename) { $this->gpxReader = new XMLReader(); $this->gpxReader->open($gpxFilename); } public function getElements($elementType) { while ($this->gpxReader->read()) { if ($this->gpxReader->nodeType == XMLREADER::ELEMENT && $this->gpxReader->name == $elementType) { $doc = new DOMDocument('1.0', 'UTF-8'); $xml = simplexml_import_dom($doc->importNode($this->gpxReader->expand(), true)); $gpxAttributes = $this->readAttributes($this->gpxReader); $gpxElement = $this->readChildren($xml); $gpxElement->position = $gpxAttributes; yield $gpxElement->timestamp => $gpxElement; } } } }
A Functional Guide to Cat Herding with PHP Generators // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Iterate over the trackpoint set from the gpx file, // displaying each point detail in turn foreach ($gpxReader->getElements('trkpt') as $time => $element) { printf( '%s' . PHP_EOL . ' latitude: %7.4f longitude: %7.4f elevation: %d' . PHP_EOL, $time->format('Y-m-d H:i:s'), $element->position->latitude, $element->position->longitude, $element->elevation ); }
A Functional Guide to Cat Herding with PHP Generators 2015-03-02 07:59:35 latitude: 54.5132, longitude: -3.0448, elevation: 0 2015-03-02 08:00:31 latitude: 54.5132, longitude: -3.0451, elevation: 0 2015-03-02 08:00:51 latitude: 54.5132, longitude: -3.0449, elevation: 192 ... 2015-03-02 14:50:39 latitude: 54.4392, longitude: -2.9714, elevation: 52 2015-03-02 14:50:49 latitude: 54.4400, longitude: -2.9722, elevation: 52
A Functional Guide to Cat Herding with PHP Generators
Cat Herding with PHP Generators – Filter • A filter selects only a subset of values from the Traversable. • The rules for filtering are defined in a callback function. • If no callback is provided, then only non-empty values are returned.
Cat Herding with PHP Generators – Filter function notEmpty($value) { return !empty($value); } /** * Version of filter to use with versions of PHP prior to 5.6.0, * without the `$flag` option * **/ function filter(Traversable $filter, Callable $callback = null) { if ($callback === null) { $callback = 'notEmpty'; } foreach ($filter as $key => $value) { if ($callback($value)) { yield $key => $value; } } }
Cat Herding with PHP Generators – Filter /** * The `$flag` option (and the constants ARRAY_FILTER_USE_KEY and ARRAY_FILTER_USE_BOTH) * were introduced in PHP 5.6.0 * **/ function filter(Traversable $filter, Callable $callback = null, $flag = 0) { if ($callback === null) { $callback = 'notEmpty'; } foreach ($filter as $key => $value) { switch($flag) { case ARRAY_FILTER_USE_KEY: if ($callback($key)) { yield $key => $value; } break; case ARRAY_FILTER_USE_BOTH: if ($callback($value, $key)) { yield $key => $value; } break; default: if ($callback($value)) { yield $key => $value; } break; } } }
Cat Herding with PHP Generators – Filter // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Define the date/time filter parameters $startTime = new DateTime('2015-03-02 13:20:00Z'); $endTime = new DateTime('2015-03-02 13:30:00Z'); // Create the filter callback with the date/time parameters we've just defined $timeFilter = function($timestamp) use ($startTime, $endTime) { return $timestamp >= $startTime && $timestamp <= $endTime; };
Cat Herding with PHP Generators – Filter // Iterate over the trackpoint set from the gpx file, // displaying each point detail in turn foreach (filter($gpxReader->getElements('trkpt'), $timeFilter, ARRAY_FILTER_USE_KEY) as $time => $element) { printf( '%s' . PHP_EOL . ' latitude: %7.4f longitude: %7.4f elevation: %d' . PHP_EOL, $time->format('Y-m-d H:i:s'), $element->position->latitude, $element->position->longitude, $element->elevation ); }
A Functional Guide to Cat Herding with PHP Generators 2015-03-02 13:20:21 latitude: 54.4692 longitude: -2.9677 elevation: 634 2015-03-02 13:21:13 latitude: 54.4691 longitude: -2.9677 elevation: 628 2015-03-02 13:21:58 latitude: 54.4690 longitude: -2.9676 elevation: 621 ... 2015-03-02 13:29:47 latitude: 54.4658 longitude: -2.9673 elevation: 533 2015-03-02 13:29:58 latitude: 54.4657 longitude: -2.9674 elevation: 531
Cat Herding with PHP Generators – Map • A map is like a foreach loop that transforms each value in the Traversable. • Each input value is transformed into a new output value. • The rules for the transformation are defined in a callback function.
Cat Herding with PHP Generators – Map function map(Callable $callback, Traversable $iterator) { foreach ($iterator as $key => $value) { yield $key => $callback($value); } }
Cat Herding with PHP Generators – Map namespace GpxReaderHelpers; class DistanceCalculator { public function setDistance(GpxReaderGpxElement $point) { $point->distance = $this->calculateDistance($point); return $point; } }
Cat Herding with PHP Generators – Map // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Set the mapper to calculate the distance between a trackpoint // and the previous trackpoint $distanceCalculator = new GpxReaderHelpersDistanceCalculator();
Cat Herding with PHP Generators – Map // Iterate over the trackpoint set from the gpx file, mapping the distances as we go, // displaying each point detail in turn foreach (map([$distanceCalculator, 'setDistance'], $gpxReader->getElements('trkpt')) as $time => $element) { printf( '%s' . PHP_EOL . ' latitude: %7.4f longitude: %7.4f elevation: %d' . PHP_EOL . ' distance from previous point: %5.2f m' . PHP_EOL, $time->format('Y-m-d H:i:s'), $element->position->latitude, $element->position->longitude, $element->elevation, $element->distance ); }
Cat Herding with PHP Generators – Map 2015-03-02 07:59:35 latitude: 54.5132 longitude: -3.0448 elevation: 0 distance from previous point: 0.00 m 2015-03-02 08:00:31 latitude: 54.5132 longitude: -3.0451 elevation: 0 distance from previous point: 17.15 m 2015-03-02 08:00:51 latitude: 54.5132 longitude: -3.0449 elevation: 192 distance from previous point: 15.74 m ... 2015-03-02 14:50:39 latitude: 54.4392 longitude: -2.9714 elevation: 52 distance from previous point: 98.87 m 2015-03-02 14:50:49 latitude: 54.4400 longitude: -2.9722 elevation: 52 distance from previous point: 106.70 m
Cat Herding with PHP Generators – Reduce • A reduce aggregates all the values in the Traversable to a single value. • A callback function determines the process for the aggregation.
Cat Herding with PHP Generators – Reduce function reduce(Traversable $iterator, Callable $callback, $initial = null) { $result = $initial; foreach($iterator as $value) { $result = $callback($result, $value); } return $result; }
Cat Herding with PHP Generators – Reduce // Reduce our trackpoint set from the gpx file (mapping the distance as we go) // and summing the results to calculate the total distance travelled $totalDistance = reduce( map([$distanceCalculator, 'setDistance'], $gpxReader->getElements('trkpt')), function($runningTotal, $value) { $runningTotal += $value->distance; return $runningTotal; }, 0.0 ); // Display the results of our reduce printf( 'Total distance travelled is %5.2f km' . PHP_EOL, $totalDistance / 1000 );
Cat Herding with PHP Generators – Map Total distance travelled is 19.27 km
A Functional Guide to Cat Herding with PHP Generators
A Functional Guide to Cat Herding with PHP Generators
A Functional Guide to Cat Herding with PHP Generators https://github.com/lstrojny/functional-php
A Functional Guide to Cat Herding with PHP Generators No cats were forced to walk anywhere that they didn't want to go during the writing of this presentation.
A Functional Guide to Cat Herding with PHP Generators ? Questions
Who am I? Mark Baker Design and Development Manager InnovEd (Innovative Solutions for Education) Ltd Coordinator and Developer of: Open Source PHPOffice library PHPExcel, PHPWord,PHPPowerPoint, PHPProject, PHPVisio Minor contributor to PHP core @Mark_Baker https://github.com/MarkBaker http://uk.linkedin.com/pub/mark-baker/b/572/171

A Functional Guide to Cat Herding with PHP Generators

  • 1.
    A Functional Guideto Cat Herding with PHP Generators The Filter/Map/Reduce Pattern for PHP Generators
  • 2.
    A Functional Guideto Cat Herding with PHP Generators • Blog Post http://markbakeruk.net/2016/01/19/a-functional-guide-to-cat-herding-with- php-generators/ • Code Examples https://github.com/MarkBaker/GeneratorFunctionExamples
  • 3.
    A Functional Guideto Cat Herding with PHP Generators
  • 4.
    A Functional Guideto Cat Herding with PHP Generators
  • 5.
    A Functional Guideto Cat Herding with PHP Generators <?xml version="1.0" encoding="ISO-8859-1"?> <gpx version="1.1" creator="Memory-Map 5.4.2.1089 http://www.memory-map.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"> <trk> <name>Wythburn</name> <type>Track</type> <trkseg> <trkpt lat="54.5131924947" lon="-3.0448236664"><time>2015-03-02T07:59:35Z</time></trkpt> <trkpt lat="54.5131921768" lon="-3.0450893323"><time>2015-03-02T08:00:31Z</time></trkpt> <trkpt lat="54.5131534894" lon="-3.0448548317"><ele>192</ele><time>2015-03- 02T08:00:51Z</time></trkpt> ... <trkpt lat="54.4399968465" lon="-2.9721705119"><ele>52</ele><time>2015-03- 02T14:50:49Z</time></trkpt> </trkseg> </trk> </gpx>
  • 6.
    A Functional Guideto Cat Herding with PHP Generators namespace GpxReader; class GpxHandler { protected $gpxReader; public function __construct($gpxFilename) { $this->gpxReader = new XMLReader(); $this->gpxReader->open($gpxFilename); } public function getElements($elementType) { while ($this->gpxReader->read()) { if ($this->gpxReader->nodeType == XMLREADER::ELEMENT && $this->gpxReader->name == $elementType) { $doc = new DOMDocument('1.0', 'UTF-8'); $xml = simplexml_import_dom($doc->importNode($this->gpxReader->expand(), true)); $gpxAttributes = $this->readAttributes($this->gpxReader); $gpxElement = $this->readChildren($xml); $gpxElement->position = $gpxAttributes; yield $gpxElement->timestamp => $gpxElement; } } } }
  • 7.
    A Functional Guideto Cat Herding with PHP Generators // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Iterate over the trackpoint set from the gpx file, // displaying each point detail in turn foreach ($gpxReader->getElements('trkpt') as $time => $element) { printf( '%s' . PHP_EOL . ' latitude: %7.4f longitude: %7.4f elevation: %d' . PHP_EOL, $time->format('Y-m-d H:i:s'), $element->position->latitude, $element->position->longitude, $element->elevation ); }
  • 8.
    A Functional Guideto Cat Herding with PHP Generators 2015-03-02 07:59:35 latitude: 54.5132, longitude: -3.0448, elevation: 0 2015-03-02 08:00:31 latitude: 54.5132, longitude: -3.0451, elevation: 0 2015-03-02 08:00:51 latitude: 54.5132, longitude: -3.0449, elevation: 192 ... 2015-03-02 14:50:39 latitude: 54.4392, longitude: -2.9714, elevation: 52 2015-03-02 14:50:49 latitude: 54.4400, longitude: -2.9722, elevation: 52
  • 9.
    A Functional Guideto Cat Herding with PHP Generators
  • 10.
    Cat Herding withPHP Generators – Filter • A filter selects only a subset of values from the Traversable. • The rules for filtering are defined in a callback function. • If no callback is provided, then only non-empty values are returned.
  • 11.
    Cat Herding withPHP Generators – Filter function notEmpty($value) { return !empty($value); } /** * Version of filter to use with versions of PHP prior to 5.6.0, * without the `$flag` option * **/ function filter(Traversable $filter, Callable $callback = null) { if ($callback === null) { $callback = 'notEmpty'; } foreach ($filter as $key => $value) { if ($callback($value)) { yield $key => $value; } } }
  • 12.
    Cat Herding withPHP Generators – Filter /** * The `$flag` option (and the constants ARRAY_FILTER_USE_KEY and ARRAY_FILTER_USE_BOTH) * were introduced in PHP 5.6.0 * **/ function filter(Traversable $filter, Callable $callback = null, $flag = 0) { if ($callback === null) { $callback = 'notEmpty'; } foreach ($filter as $key => $value) { switch($flag) { case ARRAY_FILTER_USE_KEY: if ($callback($key)) { yield $key => $value; } break; case ARRAY_FILTER_USE_BOTH: if ($callback($value, $key)) { yield $key => $value; } break; default: if ($callback($value)) { yield $key => $value; } break; } } }
  • 13.
    Cat Herding withPHP Generators – Filter // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Define the date/time filter parameters $startTime = new DateTime('2015-03-02 13:20:00Z'); $endTime = new DateTime('2015-03-02 13:30:00Z'); // Create the filter callback with the date/time parameters we've just defined $timeFilter = function($timestamp) use ($startTime, $endTime) { return $timestamp >= $startTime && $timestamp <= $endTime; };
  • 14.
    Cat Herding withPHP Generators – Filter // Iterate over the trackpoint set from the gpx file, // displaying each point detail in turn foreach (filter($gpxReader->getElements('trkpt'), $timeFilter, ARRAY_FILTER_USE_KEY) as $time => $element) { printf( '%s' . PHP_EOL . ' latitude: %7.4f longitude: %7.4f elevation: %d' . PHP_EOL, $time->format('Y-m-d H:i:s'), $element->position->latitude, $element->position->longitude, $element->elevation ); }
  • 15.
    A Functional Guideto Cat Herding with PHP Generators 2015-03-02 13:20:21 latitude: 54.4692 longitude: -2.9677 elevation: 634 2015-03-02 13:21:13 latitude: 54.4691 longitude: -2.9677 elevation: 628 2015-03-02 13:21:58 latitude: 54.4690 longitude: -2.9676 elevation: 621 ... 2015-03-02 13:29:47 latitude: 54.4658 longitude: -2.9673 elevation: 533 2015-03-02 13:29:58 latitude: 54.4657 longitude: -2.9674 elevation: 531
  • 16.
    Cat Herding withPHP Generators – Map • A map is like a foreach loop that transforms each value in the Traversable. • Each input value is transformed into a new output value. • The rules for the transformation are defined in a callback function.
  • 17.
    Cat Herding withPHP Generators – Map function map(Callable $callback, Traversable $iterator) { foreach ($iterator as $key => $value) { yield $key => $callback($value); } }
  • 18.
    Cat Herding withPHP Generators – Map namespace GpxReaderHelpers; class DistanceCalculator { public function setDistance(GpxReaderGpxElement $point) { $point->distance = $this->calculateDistance($point); return $point; } }
  • 19.
    Cat Herding withPHP Generators – Map // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Set the mapper to calculate the distance between a trackpoint // and the previous trackpoint $distanceCalculator = new GpxReaderHelpersDistanceCalculator();
  • 20.
    Cat Herding withPHP Generators – Map // Iterate over the trackpoint set from the gpx file, mapping the distances as we go, // displaying each point detail in turn foreach (map([$distanceCalculator, 'setDistance'], $gpxReader->getElements('trkpt')) as $time => $element) { printf( '%s' . PHP_EOL . ' latitude: %7.4f longitude: %7.4f elevation: %d' . PHP_EOL . ' distance from previous point: %5.2f m' . PHP_EOL, $time->format('Y-m-d H:i:s'), $element->position->latitude, $element->position->longitude, $element->elevation, $element->distance ); }
  • 21.
    Cat Herding withPHP Generators – Map 2015-03-02 07:59:35 latitude: 54.5132 longitude: -3.0448 elevation: 0 distance from previous point: 0.00 m 2015-03-02 08:00:31 latitude: 54.5132 longitude: -3.0451 elevation: 0 distance from previous point: 17.15 m 2015-03-02 08:00:51 latitude: 54.5132 longitude: -3.0449 elevation: 192 distance from previous point: 15.74 m ... 2015-03-02 14:50:39 latitude: 54.4392 longitude: -2.9714 elevation: 52 distance from previous point: 98.87 m 2015-03-02 14:50:49 latitude: 54.4400 longitude: -2.9722 elevation: 52 distance from previous point: 106.70 m
  • 22.
    Cat Herding withPHP Generators – Reduce • A reduce aggregates all the values in the Traversable to a single value. • A callback function determines the process for the aggregation.
  • 23.
    Cat Herding withPHP Generators – Reduce function reduce(Traversable $iterator, Callable $callback, $initial = null) { $result = $initial; foreach($iterator as $value) { $result = $callback($result, $value); } return $result; }
  • 24.
    Cat Herding withPHP Generators – Reduce // Reduce our trackpoint set from the gpx file (mapping the distance as we go) // and summing the results to calculate the total distance travelled $totalDistance = reduce( map([$distanceCalculator, 'setDistance'], $gpxReader->getElements('trkpt')), function($runningTotal, $value) { $runningTotal += $value->distance; return $runningTotal; }, 0.0 ); // Display the results of our reduce printf( 'Total distance travelled is %5.2f km' . PHP_EOL, $totalDistance / 1000 );
  • 25.
    Cat Herding withPHP Generators – Map Total distance travelled is 19.27 km
  • 26.
    A Functional Guideto Cat Herding with PHP Generators
  • 27.
    A Functional Guideto Cat Herding with PHP Generators
  • 28.
    A Functional Guideto Cat Herding with PHP Generators https://github.com/lstrojny/functional-php
  • 29.
    A Functional Guideto Cat Herding with PHP Generators No cats were forced to walk anywhere that they didn't want to go during the writing of this presentation.
  • 30.
    A Functional Guideto Cat Herding with PHP Generators ? Questions
  • 31.
    Who am I? MarkBaker Design and Development Manager InnovEd (Innovative Solutions for Education) Ltd Coordinator and Developer of: Open Source PHPOffice library PHPExcel, PHPWord,PHPPowerPoint, PHPProject, PHPVisio Minor contributor to PHP core @Mark_Baker https://github.com/MarkBaker http://uk.linkedin.com/pub/mark-baker/b/572/171