Fizz Buzz and Fibbonacci combined
I had a job interview recently and I was asked to do FizzBuzz.
The code is very simple to write, however I wanted to see if I could make it easily accept any series of numbers and easily alter its output.
The code I came up within an hour or so is below, but its use comes down to the simple one liners;
// Simple demo of numbers 0-5, default behaviour, no args foreach(NumbersDirector::create() AS $number) echo "$number,"; |
Output: 0, 1, 2, 3, 4, 5
// Fizz Buzz from 1-100 using fizzbuzz factory builder foreach(NumbersDirector::create(new NumericalSeries(array('start'=>1,'end'=>100)),new FizzBuzzBuilder()) AS $number) echo "$number,"; |
Output: 1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,16,17,Fizz,19,Buzz,Fizz,22,23,Fizz,Buzz,26,Fizz,28,29,FizzBuzz,………………..
// Fibo from 0-2000 foreach(NumbersDirector::create(new FibbonacciSeries(array('end'=>2000))) AS $number) echo "$number,"; |
Output: 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,
// Fibo from 2000-50000000 using fizzbuzz factory builder foreach(NumbersDirector::create(new FibbonacciSeries(array('start'=>2000,'end'=>50000000)),new FizzBuzzBuilder()) AS $number) echo "$number,"; |
Output: Fizz,5998,9997,Buzz,Fizz,41987,67979,109966,FizzBuzz,287911,465856,753767,Fizz,Buzz,3193013,5166403,Fizz,13525819,Buzz,35411054,Fizz,
As you can see the last one is passing in a custom series and modifying the output to FizzBuzz.
It uses several patterns, ReadOnly, Factory, Director, Builder and returns an iterable instance, just a coding exercise really.
Full code below if anyone would care to see:
<?php /* Traits */ Trait Iterable{ protected $_position; protected $_data; public function rewind() { $this->_position = 0; } public function current() { if ($this->valid()){ $data = $this->getData(); return $data[$this->_position]; } } public function key() { return $this->_position; } public function next() { ++$this->_position; } public function valid() { $data = $this->getData(); return isset($data[$this->_position]); } protected function setData(array $data = null){ $this->_data = $data; $this->rewind(); return $this; } public function getData(){ if (!isset($this->_data)){ $this->setData(); } return $this->_data; } public function getStart(){ if (isset($this->_data[0])){ return $this->_data[0]; } return null; } public function getEnd(){ $count = count($this->_data)-1; if ($count>0){ return $this->_data[$count]; } return null; } /* * Required by countable */ public function count(){ return count($this->_data); } } trait Readonly{ public function __construct(array $options = null) { if (is_array($options)) { $this->setOptions($options); } } public function __set($name, $value) { $method = 'set' . $name; if (method_exists($this, $method)) { $this->$method($value); return $this; } throw new \Exception('"'.$name.'" is an invalid property of '.__CLASS__.' assignment of "'.$value.'" failed!'); } public function __get($name) { $method = 'get' . $name; if (method_exists($this, $method)) { return $this->$method(); } throw new \Exception('Invalid '.__CLASS__.' property '.$name); } public function setOptions(array $options) { $methods = get_class_methods($this); foreach ($options as $key => $value) { $method = 'set' . ucfirst($key); if (in_array($method, $methods)) { $this->$method($value); } } return $this; } } /* Interfaces */ Interface SeriesInterface { public function generate(); } Interface BuildersInterface { public function build($entry); } Interface ValueInterface { public function getValue(); public function setValue(); public function __toString(); } /* Abstract classes */ abstract class Series implements SeriesInterface { use Readonly; protected $_start; protected $_end; public function setStart($start = 0){ $this->_start = $start; return $this; } public function getStart(){ if (!isset($this->_start)){ $this->setStart(); } return $this->_start; } public function setEnd($end = 5){ $this->_end = $end; return $this; } public function getEnd(){ if (!isset($this->_end)){ $this->setEnd(); } return $this->_end; } abstract function generate(); } abstract class Value implements ValueInterface { use Readonly; protected $_val; public function getValue(){ if (!isset($this->_val)){ $this->setValue(); } return $this->_val; } public function setValue($val = 0){ if (!is_int($val)){ throw new Exception('Expected integer as value got '.getType($val)); // would normally be the signature } $this->_val = $val; return $this; } abstract public function __toString(); } /* Classes */ class NumbersDirector implements Iterator{ use Iterable; protected function __construct(){} public function create(SeriesInterface $series = null, BuildersInterface $builder = null){ if (is_null($series)){ $series = new NumericalSeries(); } if (is_null($builder)){ $builder = new NumericalBuilder(); } $stack = array(); foreach($series->generate() AS $entry){ $stack[]= $builder->build($entry); } $me = new self(); $me->setData($stack); return $me; } } class NumericalSeries extends Series { public function generate(){ $series = array(); for ($x=$this->start; $x<($this->end + 1); $x++){ $series[] = $x; } return $series; } } class FibbonacciSeries extends Series { /** * Fibonacci using Binet's formula * @link http://mathworld.wolfram.com/BinetsFibonacciNumberFormula.html */ public function binet($n) { $phi = (1 + sqrt(5)) / 2; return (pow($phi, $n) - pow(1 - $phi, $n)) / sqrt(5); } public function setStart($start = 1){ $this->_start = $start; return $this; } public function setEnd($end = 55){ $this->_end = $end; return $this; } public function generate(){ $series = array(); $fib = [$this->start,$this->start - 1]; $next = 0; while($next < $this->end) { $next = array_sum($fib); array_shift($fib); array_push($fib,$next); $series[]=$next; } return $series; } } class NumericalBuilder implements BuildersInterface { public function Build($entry){ $number = new Number(); $number->value = $entry; return $number; } } class FizzBuzzBuilder implements BuildersInterface { public function Build($entry){ /* Factory method */ $class = ""; if ($entry%3==0){ $class.= 'Fizz'; } if ($entry%5==0){ $class.= 'Buzz'; } if (!class_exists($class) || !isset(class_implements($class)['ValueInterface'])){ $class = "Number"; } $number = new $class(); $number->value = $entry; return $number; } } class Number extends Value { public function __toString(){ return (string) $this->getValue(); } } class Buzz extends Value { public function __toString(){ return (string) 'Buzz'; } } class Fizz extends Value { public function __toString(){ return (string) 'Fizz'; } } class FizzBuzz extends Value { public function __toString(){ return (string) 'FizzBuzz'; } } // Simple demo of numbers 0-5, default behaviour, no args foreach(NumbersDirector::create() AS $number) echo "$number,"; echo "<hr />"; // Fizz Buzz from 1-100 using fizzbuzz factory builder foreach(NumbersDirector::create(new NumericalSeries(array('start'=>1,'end'=>100)),new FizzBuzzBuilder()) AS $number) echo "$number,"; echo "<hr />"; // Fibo from 0-2000 foreach(NumbersDirector::create(new FibbonacciSeries(array('end'=>2000))) AS $number) echo "$number,"; echo "<hr />"; // Fibo from 2000-50000000 using fizzbuzz factory builder foreach(NumbersDirector::create(new FibbonacciSeries(array('start'=>2000,'end'=>50000000)),new FizzBuzzBuilder()) AS $number) echo "$number,"; |
496 views
This entry was posted on Thursday, May 11th, 2017 at 4:28 pm and is filed under Php, Programming. You can follow any responses to this entry through the RSS 2.0 feed.