49
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

Embed Size (px)

Citation preview

Page 1: A Functional Guide to Cat Herding with PHP Generators

A Functional Guide to Cat Herding with

PHP Generators The Filter/Map/Reduce Pattern for PHP

Generators

Page 2: A Functional Guide to Cat Herding with 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 Exampleshttps://github.com/MarkBaker/GeneratorFunctionExamples

Page 3: A Functional Guide to Cat Herding with PHP Generators

A Functional Guide to Cat Herding with PHP Generators

Page 4: A Functional Guide to Cat Herding with PHP Generators

A Functional Guide to Cat Herding with PHP Generators

Page 5: A Functional Guide to Cat Herding with PHP Generators

A Functional Guide to Cat Herding with PHP Generators<?xml version="1.0" encoding="UTF-8" standalone="no" ?><gpx 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>Roman 2015-11-23</name> <trkseg> <trkpt lat="53.54398800" lon="-2.7403680000"> <ele>146.100</ele><time>2015-11-23T11:05:07Z</time> </trkpt> <trkpt lat="53.54401600" lon="-2.7402620000"> <ele>141.300</ele><time>2015-11-23T11:05:15Z</time> </trkpt>

...

<trkpt lat="53.54384500" lon="-2.7426130000"> <ele>110.500</ele><time>2015-11-23T14:58:57Z</time> </trkpt> </trkseg> </trk></gpx>

Page 6: A Functional Guide to Cat Herding with PHP Generators

A Functional Guide to Cat Herding with PHP Generatorsnamespace 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; } } }}

Page 7: A Functional Guide to Cat Herding with PHP Generators

A Functional Guide to Cat Herding with PHP Generators

// Create our initial Generator to read the gpx file$gpxReader = new GpxReader\GpxHandler($gpxFilename);

// Iterate over the trackpoint set from the gpx file,// displaying each point detail in turnforeach ($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 );}

Page 8: A Functional Guide to Cat Herding with PHP Generators

A Functional Guide to Cat Herding with PHP Generators2015-11-23 11:05:07 latitude: 53.5440 longitude: -2.7404 elevation: 1462015-11-23 11:05:15 latitude: 53.5440 longitude: -2.7403 elevation: 1412015-11-23 11:05:25 latitude: 53.5440 longitude: -2.7402 elevation: 140

...

2015-11-23 14:58:47 latitude: 53.5439 longitude: -2.7426 elevation: 1032015-11-23 14:58:57 latitude: 53.5438 longitude: -2.7426 elevation: 110

Page 9: A Functional Guide to Cat Herding with PHP Generators

A Functional Guide to Cat Herding with PHP Generators

Page 10: 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.

Page 11: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filterarray_filter()

Filters elements of an array using a callback function.

array array_filter ( array $array [, callable $callback] )

Iterates over each value in the array passing them to the callback function. If the callback function returns true, the current value from array is returned into the result array, otherwise it is discarded. Array keys are preserved.

Page 12: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filterfunction 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; } }}

Page 13: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filterarray_filter()

Filters elements of an array using a callback function.

array array_filter ( array $array [, callable $callback] )array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )

Iterates over each value in the array passing them to the callback function. If the callback function returns true, the current value from array is returned into the result array, otherwise it is discarded. Array keys are preserved.

[PHP < 5.6.0][PHP >= 5.6.0]

Page 14: A Functional Guide to Cat Herding with PHP Generators

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: ... case ARRAY_FILTER_USE_BOTH: ... default: ... } } }

Page 15: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filter• Time Range

Where was Roman between 11:30 and 12:00?

• Geo-Fencing (“Bounding Box”)• Inside

Did Osiris go anywhere near the main road?• Outside

Has Lexie left the house at all?

Page 16: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filter// Create our initial Generator to read the gpx file$gpxReader = new GpxReader\GpxHandler($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;};

Page 17: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filter// Iterate over the trackpoint set from the gpx file,// displaying each point detail in turnforeach (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 );}

Page 18: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filter

Page 19: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filternamespace GpxReader\Helpers;

class BoundingBox { /** * Identify whether a trackpoint falls inside the defined bounding box * * @param \GpxReader\GpxElement The trackpoint * @return boolean If the trackpoint falls outside (false) * or inside (true) the bounding box **/ public function inside(\GpxReader\GpxElement $point) { return (($point->position->longitude >= $this->left) && ($point->position->longitude <= $this->right) && ($point->position->latitude >= $this->bottom) && ($point->position->latitude <= $this->top)); }}

Page 20: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filter// Create our initial Generator to read the gpx file$gpxReader = new GpxReader\GpxHandler($gpxFilename);

// Create a bounding box defining the coordinates we want to test each point against// This bounding box is for inside the house/garden$boundaries = new GpxReader\Helpers\BoundingBox(); $boundaries->setLatitudes(53.54382, 53.54340); $boundaries->setLongitudes(-2.74059, -2.74005);

// We want to set the filter to include only points inside the bounding box$boundingBoxFilter = [$boundaries, 'inside'];

Page 21: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filter// Iterate over the trackpoint set from the gpx file,// displaying each point detail in turnforeach (filter($gpxReader->getElements('trkpt'), $boundingBoxFilter) 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 );}

Page 22: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Filter// Iterate over the trackpoint set from the gpx file, // displaying each point detail in turn// applying both a time filter (12:00:00-12:20:00 on 2015-11-23) // and a bounding box filter for inside the house/gardenforeach (filter( filter( $gpxReader->getElements('trkpt'), $timeFilter, ARRAY_FILTER_USE_KEY ), $boundingBoxFilter ) as $time => $element) { printf( ... ); }

Page 23: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators

OsirisLocated

AchievementUnlocked

Page 24: A Functional Guide to Cat Herding with PHP Generators

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.

Page 25: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Maparray_map()

Applies the callback to the elements of the given arrays.

array array_map ( callable $callback , array $array1 [, array $... ] )

array_map() returns an array containing all the elements of array1 after applying the callback function to each one. The number of parameters that the callback function accepts should match the number of arrays passed to the array_map().

Page 26: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Mapfunction map(Callable $callback, Traversable $iterator) { foreach ($iterator as $key => $value) { yield $key => $callback($value); } }

Page 27: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Mapnamespace GpxReader\Helpers;

class DistanceCalculator { public function setDistance(\GpxReader\GpxElement $point) { $point->distance = $this->calculateDistance($point); return $point; } }

Page 28: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Map// Create our initial Generator to read the gpx file$gpxReader = new GpxReader\GpxHandler($gpxFilename);

// Set the mapper to calculate the distance between a trackpoint// and the previous trackpoint$distanceCalculator = new GpxReader\Helpers\DistanceCalculator();

Page 29: A Functional Guide to Cat Herding with PHP Generators

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 turnforeach (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 );}

Page 30: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Mapfunction mmap(Callable $callback, Traversable ...$iterators) { $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); foreach($iterators as $iterator) { $mi->attachIterator($iterator); } foreach($mi as $values) { yield $callback(...$values); }}

http://nl3.php.net/manual/en/function.array-slice.php

...Splat OperatorPHP >= 5.6.0Argument Packing/Unpacking

Page 31: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators

LexieLocated

AchievementUnlocked

Page 32: A Functional Guide to Cat Herding with PHP Generators

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.

Page 33: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Reducearray_reduce()

Iteratively reduces the array to a single value using a callback function.

mixed array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] )

array_reduce() applies the callback function iteratively to the elements of the array, so as to reduce the array to a single value.

Page 34: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Reducefunction reduce(Traversable $iterator, Callable $callback, $initial = null) { $result = $initial; foreach($iterator as $value) { $result = $callback($result, $value); } return $result; }

Page 35: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Reduce• Bounding Box

What Coordinates should I use for the bounding box when displaying the track on a map?

• DistanceHow far has Roman walked while he’s been out?What is the furthest distance that Osiris has travelled from home?

Page 36: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Reduce// Iterate over our trackpoint set from the gpx file (mapping the distance as we go)// and reducing 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 reduceprintf( 'Total distance travelled is %5.2f km' . PHP_EOL, $totalDistance / 1000);

Page 37: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – ReduceTotal distance travelled is 4.33 km

Page 38: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Reducenamespace GpxReader\Helpers;

class BoundingBox { function calculate($discard, \GpxReader\GpxElement $point) { $this->top = max($point->position->latitude, $this->top); $this->bottom = min($point->position->latitude, $this->bottom); $this->left = min($point->position->longitude, $this->left); $this->right = max($point->position->longitude, $this->right); return $this; } }

Page 39: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – Reduce// Create our initial Generator to read the gpx file$gpxReader = new GpxReader\GpxHandler($gpxFilename);

// Set our bounding box callback$boundaries = new GpxReader\Helpers\BoundingBox();

// Reduce our trackpoint set from the gpx file against the bounding box callback$boundingBox = reduce( $gpxReader->getElements('trkpt'), [$boundaries, 'calculate'] );

// Display the results of our reduceprintf( 'Top: %7.4f Bottom: %7.4f' . PHP_EOL . 'Left: %7.4f Right: %7.4f' . PHP_EOL, $boundingBox->top, $boundingBox->bottom, $boundingBox->left, $boundingBox->right);

Page 40: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators – ReduceTop: 53.5445 Bottom: 53.5426Left: -2.7433 Right: -2.7393

Page 41: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators

RomanLocated

AchievementUnlocked

Page 42: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generatorsiterator_apply()

Call a function for every element in an iterator.

int iterator_apply ( Traversable $iterator , callable $function [, array $args ] )

Page 43: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generatorsiterator_count()

Count the elements in an iterator.

int iterator_count ( Traversable $iterator )

Page 44: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generatorsiterator_to_array()

Copy all values from the iterator into an array.

array iterator_to_array ( Traversable $iterator [, bool $use_keys = true ] )

Page 45: A Functional Guide to Cat Herding with PHP Generators

Cat Herding with PHP Generators// Create an instance of the fluent Generator Helper and set our filters, // mapper offset and limits// and the display callback to "do" (or execute) // In this case, the "do" is a display callback, // but it could also be a "reduce" callbackwithGenerator($trackPoints) ->filteredBy($boundingBoxFilter) ->filteredBy($timeFilter, ARRAY_FILTER_USE_KEY) ->map([$distanceCalculator, 'setDistance']) ->offset(1) ->limit(1) ->do($display);

Page 46: A Functional Guide to Cat Herding with PHP Generators

A Functional Guide to Cat Herding with PHP Generatorshttps://github.com/lstrojny/functional-php

Page 47: A Functional Guide to Cat Herding with PHP Generators

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.

Page 48: A Functional Guide to Cat Herding with PHP Generators

A Functional Guide to Cat Herding with PHP Generators

?Questions

Page 49: A Functional Guide to Cat Herding with PHP Generators

Who am I?Mark Baker

Design and Development ManagerInnovEd (Innovative Solutions for Education) Ltd

Coordinator and Developer of:Open Source PHPOffice library

PHPExcel, PHPWord, PHPPresentation (formerly PHPPowerPoint), PHPProject, PHPVisioMinor contributor to PHP core

@Mark_Baker

https://github.com/MarkBaker

http://uk.linkedin.com/pub/mark-baker/b/572/171

http://markbakeruk.net