What you're really looking for is a way to iterate sequences:
000
001
010
011
100
101
110
111
It would be nice too if we didn't have to make the assumption that the size of each input array is the same. So if we decrease the size of the second array by 1:
array(
[0] => array([0]=>'blue',[1]=>'red'),
[1] => array([0]=>'sunny'),
[2] => array([0]=>'sweet',[1]=>'acid');
...we want the maximum value for that column to decrease by 1:
000
001
100
101
This abstraction makes the problem easier to think about. How would you iterate this sequence? On each iteration, you increase the rightmost column by 1. If doing so would increase it beyond its maximum, reset it 0 and move left a column. Now you repeat what you just did on the last column. If you can't increase this column either, reset it to 0, move left, rinse, and repeat. If you move all the way across and haven't been able to increase any column without going beyond its maximum, you're done.
We can wrap the above logic in a PHP iterator:
class Sequence implements Iterator {
private $input;
private $hasNext;
private $positions;
public function __construct(array $input) {
$this->input = $input;
}
public function rewind() {
$this->hasNext = true;
$this->positions = array();
for ($i = 0; $i < count($this->input); $i++) {
$this->positions[$i] = 0;
}
}
public function valid() {
return $this->hasNext;
}
public function current() {
$current = array();
for ($i = 0; $i < count($this->positions); $i++) {
$current[] = $this->input[$i][$this->positions[$i]];
}
return $current;
}
public function key() {}
public function next() {
for ($i = count($this->positions) - 1; $i >= 0; $i--) {
if ($this->positions[$i] < count($this->input[$i]) - 1) {
$this->positions[$i]++;
break;
} else {
$this->positions[$i] = 0;
$this->hasNext = $i !== 0;
}
}
}
}
next()
is the implementation of the above logic. reset()
simply sets each column back to 0 and current()
uses the current sequence as the indexes of the input to return the current values.
Here it is in action (with "cloudy" removed to show the generality of the solution):
$input = array(
array('blue', 'red'),
array('sunny'),
array('sweet', 'acid')
);
$lst = new Sequence($input);
foreach ($lst as $elt) {
print(implode(', ', $elt) . "\n");
}
And its output:
blue, sunny, sweet
blue, sunny, acid
red, sunny, sweet
red, sunny, acid