Convolution, or more often zip is the process of combining each nth element of all given arrays. In fact, this is exactly what we did by passing null value to the array_map function before:
<?php
print_r(array_map(null, [1, 2], ['one', 'two'], ['un', 'deux']));
And the output:
Array (
[0] => Array (
[0] => 1 [1] => one [2] => un )
[1] => Array (
[0] => 2 [1] => two [2] => deux )
)
It is important to note that if the arrays are of different lengths, PHP will use null as the padding value:
<?php
$numerals = [1, 2, 3, 4];
$english = ['one', 'two'];
$french = ['un', 'deux', 'trois'];
print_r(array_map(null, $numerals, $english, $french));
Be aware that in most programming languages, including Haskell, Scala, and Python, the zip operation will, however, stop at the shortest array without padding any values. You can try to implement a similar function in PHP using, for example, the array_slice function to reduce all arrays to the same size before calling the array_merge function.
We can also perform the inverse operation by creating multiple arrays from an array of arrays. This process is sometimes called unzip. Here is a naive implementation which is missing a lot of checks to make it robust enough for production use:
<?php
function unzip(array $data): array {
$return = [];
$data = array_values($data);
$size = count($data[0]);
foreach($data as $child) {
$child = array_values($child);
for($i = 0; $i < $size; ++$i) {
if(isset($child[$i]) && $child[$i] !== null) { $return[$i][] = $child[$i];
} } }
return $return;
}
You could use it like this:
$zipped = array_map(null, $numerals, $english, $french);
list($numerals2, $english2, $french2) = unzip($zipped);
var_dump($numerals == $numerals2);
// bool(true)
var_dump($english == $english2);
// bool(true)
var_dump($french == $french2);
// bool(true)
Recursion
In the academic sense, recursion is the idea of dividing a problem into smaller instances of the same problem. For example, if you need to scan a directory recursively, you first scan the starting directory and then scan its children and the children's children. Most
programming languages support recursion by allowing a function to call itself. This idea is
Let's see how we can scan a directory by using recursion:
<?php
function searchDirectory($dir, $accumulator = []) { foreach (scandir($dir) as $path) {
// Ignore hidden files, current directory and parent directory if(strpos($path, '.') === 0) {
continue;
}
$fullPath = $dir.DIRECTORY_SEPARATOR.$path;
if(is_dir($fullPath)) {
$accumulator = searchDirectory($path, $accumulator);
} else {
$accumulator[] = $fullPath;
} }
return $accumulator;
}
We start by using the scandir function to obtain all files and directories. Then, if we encounter a child directory, we call the function on it again. Otherwise, we simply add the file to the accumulator. This function is recursive because it calls itself.
You could write this using control structures, but as you don't know in advance what the depth of your folder hierarchy is, the code will probably be a lot messier and harder to understand.
Some books and tutorials use the Fibonacci sequence, or computing a factorial as recursion examples but, to be fair, those are quite poor, as they are better implemented using a traditional for loop for the second, and compute terms in advance for the first.
Instead, let's wrap our heads around a more interesting challenge, the Hanoi Towers. For those unaware of this game, the traditional version features three rods with discs of
different sizes stacked in top of one another, the smallest on the top. At the beginning of the game, all discs are on the leftmost rod and the goal is to bring them to the rightmost one.
The game obeys the following rules:
Only one disc can move at a time
Only the topmost disc of a rod can be moved A disc cannot be placed on top of a smaller disc
The setup for this game looks like the following:
If we want to solve the game, the larger disc must be placed first on the last rod. In order to do that, we need to move all other discs to the middle rod first. Following this line of reasoning, we can draw three big steps that we must achieve:
Move all discs but the bigger one to the middle.
1.
Move the large disc to the right.
2.
Move all discs on top of the large one.
3.
Steps 1 and 3 are smaller versions of the initial problem. Each of those steps can, in turn, be reduced to a smaller version until we have only one disc to move–the perfect situation for a recursive function. Let's try implementing that.
To avoid cluttering our function with variables related to the rods and discs, we will assume the computer will give orders to someone making the moves. In our code, we will also assume the largest disc is number 1, smaller discs having larger numbers:
<?php
function hanoi(int $disc, string $source, string $destination, string
{ hanoi($disc - 1, $source, $via, $destination);
// step 2 : move the last disc to the destination
On using the hanoi(3, 'left', 'right', 'middle') input for the three discs, we get the following output:
Move a disc from the left rod to the right rod Move a disc from the left rod to the middle rod Move a disc from the right rod to the middle rod Move a disc from the left rod to the right rod Move a disc from the middle rod to the left rod Move a disc from the middle rod to the right rod Move a disc from the left rod to the right rod
It takes a while to think in terms of recursion instead of using a more traditional loop, and obviously recursion is not a silver bullet that is better for all problems you are trying to solve.
Some functional languages have no loop structures at all, forcing you to use recursion. This is not the case with PHP, so let's use the right tool for the job. If you can think of the
problem as a combination of smaller similar issues, usually it will be easy to use recursion.
For example, trying to find an iterative solution to the Towers of Hanoi requires a lot of careful thinking. Or you could try to rewrite the directory scanning function using only loops to convince yourself.
Some other areas where recursion is useful are:
Generating the data structure for a menu with multiple levels Traversing an XML document
Rendering a series of CMS components that could contain child components A good rule of thumb is to try recursion when your data has a tree-like structure with a root node and children.
Although often easier to read, once you have gotten to grip with it, recursion comes with a memory cost. In most applications, you should not encounter any difficulties, but we will discuss the topic further in Chapter 10, PHP Frameworks and FP, and present some methods to avoid those issues.