Вот что я мог придумать:
function inject($elem, $array) {
return array_map(function ($n) use ($elem) { return array_merge((array)$elem, (array)$n); }, $array);
}
function zip($array1, $array2) {
return array_reduce($array1, function ($v, $n) use ($array2) { return array_merge($v, inject($n, $array2)); }, array());
}
function cartesian_product($array) {
$keys = array_keys($array);
$prod = array_shift($array);
$prod = array_reduce($array, 'zip', $prod);
return array_map(function ($n) use ($keys) { return array_combine($keys, $n); }, $prod);
}
(Использование псевдомассива / списка / словарной нотации ниже, поскольку PHP просто слишком многословен для таких вещей.)
Функция inject
преобразует a, [b]
в [(a,b)]
, то есть вводит одно значение в каждое значение массива, возвращая массив массивов. Не имеет значения, является ли a
или b
массивом, он всегда будет возвращать двумерный массив.
inject('a', ['foo', 'bar'])
=> [('a', 'foo'), ('b', 'bar')]
Функция zip
применяет функцию inject
к каждому элементу в массиве.
zip(['a', 'b'], ['foo', 'bar'])
=> [('a', 'foo'), ('a', 'bar'), ('b', 'foo'), ('b', 'bar')]
Обратите внимание, что это на самом деле производит декартово произведение, поэтому zip
является небольшим неправильным значением. Простое применение этой функции ко всем элементам последовательного набора данных дает вам декартово произведение для массива любой длины.
zip(zip(['a', 'b'], ['foo', 'bar']), ['42', '76'])
=> [('a', 'foo', '42'), ('a', 'foo', '76'), ('a', 'bar', '42'), …]
Это не содержит ключей, но так как все элементы находятся в порядке в наборе результатов, вы можете просто повторно ввести ключи в результат.
array_combine(['key1', 'key2', 'key3'], ['a', 'foo', '42'])
=> [ key1 : 'a', key2 : 'foo', key3 : '42' ]
Применение этого ко всем элементам в продукте дает желаемый результат.
Вы можете свести вышеупомянутые три функции в одно длинное выражение, если хотите (что также прояснит ошибки).
«Развернутая» версия без анонимных функций для PHP <= 5.2 будет выглядеть так: </p>
function inject($elem, $array) {
$elem = (array)$elem;
foreach ($array as &$a) {
$a = array_merge($elem, (array)$a);
}
return $array;
}
function zip($array1, $array2) {
$prod = array();
foreach ($array1 as $a) {
$prod = array_merge($prod, inject($a, $array2));
}
return $prod;
}
function cartesian_product($array) {
$keys = array_keys($array);
$prod = array_shift($array);
$prod = array_reduce($array, 'zip', $prod);
foreach ($prod as &$a) {
$a = array_combine($keys, $a);
}
return $prod;
}