其他答案很好地证明了array_walk(就地修改)和array_map(返回修改后的副本)之间的区别。但是,他们并没有真正提到array_reduce,这是理解array_map 和array_filter 的一种启发性方式。
array_reduce 函数接受一个数组、一个双参数函数和一个“累加器”,如下所示:
array_reduce(array('a', 'b', 'c', 'd'),
'my_function',
$accumulator)
数组的元素使用给定的函数一次与累加器组合。上面调用的结果和这样做是一样的:
my_function(
my_function(
my_function(
my_function(
$accumulator,
'a'),
'b'),
'c'),
'd')
如果您更喜欢从循环的角度来考虑,这就像执行以下操作(实际上,当 array_reduce 不可用时,我将其用作后备):
function array_reduce($array, $function, $accumulator) {
foreach ($array as $element) {
$accumulator = $function($accumulator, $element);
}
return $accumulator;
}
这个循环版本清楚地说明了为什么我将第三个参数称为“累加器”:我们可以使用它来累积每次迭代的结果。
那么这与array_map 和array_filter 有什么关系呢?事实证明它们都是一种特殊的array_reduce。我们可以这样实现它们:
array_map($function, $array) === array_reduce($array, $MAP, array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())
忽略 array_map 和 array_filter 以不同顺序获取参数的事实;这只是 PHP 的另一个怪癖。重要的一点是,除了我称为 $MAP 和 $FILTER 的函数之外,右侧是相同的。那么,它们长什么样子呢?
$MAP = function($accumulator, $element) {
$accumulator[] = $function($element);
return $accumulator;
};
$FILTER = function($accumulator, $element) {
if ($function($element)) $accumulator[] = $element;
return $accumulator;
};
如您所见,这两个函数都接收 $accumulator 并再次返回它。这些功能有两个区别:
- $MAP 将始终附加到 $accumulator,但 $FILTER 只有在 $function($element) 为 TRUE 时才会这样做。
- $FILTER 附加原始元素,但 $MAP 附加 $function($element)。
请注意,这远非无用的琐事;我们可以用它来提高我们的算法效率!
我们经常可以看到类似这两个例子的代码:
// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))
// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')
使用 array_map 和 array_filter 代替循环使这些示例看起来非常漂亮。但是,如果 $inputs 很大,它可能会非常低效,因为第一次调用(map 或 filter)将遍历 $inputs 并构建一个中间数组。这个中间数组直接传递给第二次调用,它会再次遍历整个东西,然后中间数组需要被垃圾回收。
我们可以利用 array_map 和 array_filter 都是 array_reduce 的例子来摆脱这个中间数组。通过组合它们,我们只需在每个示例中遍历 $inputs 一次:
// Transform valid inputs
array_reduce($inputs,
function($accumulator, $element) {
if (valid($element)) $accumulator[] = transform($element);
return $accumulator;
},
array())
// Get all numeric IDs
array_reduce($inputs,
function($accumulator, $element) {
$id = get_id($element);
if (is_numeric($id)) $accumulator[] = $id;
return $accumulator;
},
array())
注意:我上面的array_map 和array_filter 的实现与PHP 的行为不完全一样,因为我的array_map 一次只能处理一个数组,而我的array_filter 不会使用“空”作为其默认的$ 函数。此外,两者都不会保留密钥。
让它们表现得像 PHP 并不难,但我觉得这些复杂性会使核心思想更难被发现。