A Functional Guide to Cat Herding with PHP Generators

Post on 18-Jan-2017

468 views 0 download

Transcript of 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

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

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="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>

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; } } }}

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

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

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 – 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.

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; } }}

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]

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: ... } } }

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?

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;};

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

Cat Herding with PHP Generators – Filter

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

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'];

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

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

Cat Herding with PHP Generators

OsirisLocated

AchievementUnlocked

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 – 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().

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

Cat Herding with PHP Generators – Mapnamespace GpxReader\Helpers;

class DistanceCalculator { public function setDistance(\GpxReader\GpxElement $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 GpxReader\GpxHandler($gpxFilename);

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

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

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

Cat Herding with PHP Generators

LexieLocated

AchievementUnlocked

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 – 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.

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; }

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?

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);

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

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; } }

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);

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

Cat Herding with PHP Generators

RomanLocated

AchievementUnlocked

Cat Herding with PHP Generatorsiterator_apply()

Call a function for every element in an iterator.

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

Cat Herding with PHP Generatorsiterator_count()

Count the elements in an iterator.

int iterator_count ( Traversable $iterator )

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 ] )

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);

A Functional Guide to Cat Herding with PHP Generatorshttps://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 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