Answers:
array_map
可以时不能更改输入数组内的值array_walk
;特别是,array_map
永远不要更改其参数。array_map
不能使用阵列键操作,array_walk
可以。array_map
返回一个新数组,array_walk
仅返回true
。因此,如果您不想由于遍历一个数组而创建一个数组,则应使用array_walk
。array_map
它还可以接收任意数量的数组,并且可以并行地遍历它们,而array_walk
只能在一个数组上操作。array_walk
可以接收一个额外的任意参数以传递给回调。自PHP 5.3(引入匿名函数时)以来,这几乎无关紧要。array_map
长度与最大输入数组的长度相同;array_walk
不返回数组,但同时不能更改原始数组的元素数;array_filter
根据过滤功能仅选择数组元素的子集。它确实保留了密钥。例:
<pre>
<?php
$origarray1 = array(2.4, 2.6, 3.5);
$origarray2 = array(2.4, 2.6, 3.5);
print_r(array_map('floor', $origarray1)); // $origarray1 stays the same
// changes $origarray2
array_walk($origarray2, function (&$v, $k) { $v = floor($v); });
print_r($origarray2);
// this is a more proper use of array_walk
array_walk($origarray1, function ($v, $k) { echo "$k => $v", "\n"; });
// array_map accepts several arrays
print_r(
array_map(function ($a, $b) { return $a * $b; }, $origarray1, $origarray2)
);
// select only elements that are > 2.5
print_r(
array_filter($origarray1, function ($a) { return $a > 2.5; })
);
?>
</pre>
结果:
Array
(
[0] => 2
[1] => 2
[2] => 3
)
Array
(
[0] => 2
[1] => 2
[2] => 3
)
0 => 2.4
1 => 2.6
2 => 3.5
Array
(
[0] => 4.8
[1] => 5.2
[2] => 10.5
)
Array
(
[1] => 2.6
[2] => 3.5
)
array_map(callback($key, $value), array_keys($array), $array)
将函数映射到数据数组的想法来自函数编程。您不应该将其array_map
视为foreach
在数组的每个元素上调用函数的循环(即使这是实现它的方式)。应该考虑将函数独立地应用于数组中的每个元素。
从理论上讲,诸如函数映射之类的事情可以并行完成,因为应用于数据的函数应该只影响数据,而不影响全局状态。这是因为array_map
可以选择将功能应用到项目的顺序(即使在PHP中不是这样)。
array_walk
另一方面,它是处理数据数组的完全相反的方法。它使用状态(&$userdata
)而不是分别处理每个项目,并且可以就地编辑该项目(很像foreach循环)。由于每次将项目$funcname
应用到它,它可能会更改程序的全局状态,因此需要一种正确的方式来处理项目。
早在PHP的土地,array_map
以及array_walk
几乎相同,除了array_walk
让你更好地控制数据的重复,并且通常用来“改变”数据就地和回访新的“改变”阵列。
array_filter
确实是array_walk
(或array_reduce
)的应用,或多或少只是为了方便而提供的。
array_filter()
使用来实现array_walk()
吗?
从文档中
bool array_walk(array&$ array,callback $ funcname [,mixed $ userdata])<-返回布尔值
array_walk接受一个数组和一个函数F
,并通过用替换每个元素x对其进行修改F(x)
。
array array_map(callback $ callback,array $ arr1 [,array $ ...])<-返回数组
array_map会做完全相同的事情,所不同的是,它会返回带有转换后元素的新数组,而不是就地修改。
array array_filter(array $ input [,callback $ callback])<-返回数组
具有function的array_filterF
,而不是转换元素,将删除所有F(x)
不正确的元素
array_walk
返回的数组类似,array_map
并认为问题出在我的函数中。直到我看到这才知道返回类型为布尔值。
其他答案很好地说明了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,然后再次返回它。这些功能有两个区别:
请注意,这绝非无用的琐事。我们可以使用它来提高算法效率!
我们经常可以看到类似于以下两个示例的代码:
// 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大,则效率可能非常低,因为第一个调用(映射或过滤器)将遍历$ 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不会将“空”用作其默认$ function。同样,它们都不会保留密钥。
使它们像PHP一样行为并不难,但我认为这些复杂性将使核心思想更难发现。
以下修订旨在更清楚地描述PHP的array_filer(),array_map()和array_walk(),所有这些源于函数式编程:
array_filter()过滤掉数据,从而产生一个仅包含前一个数组所需项的新数组,如下所示:
<?php
$array = array(1, "apples",2, "oranges",3, "plums");
$filtered = array_filter( $array, "ctype_alpha");
var_dump($filtered);
?>
现场代码在这里
所有数值都从$ array中过滤掉,而$ filtered仅带有水果类型。
array_map()还会创建一个新数组,但与array_filter()不同,由于对每个元素应用了回调,结果数组包含输入$ filtered的每个元素,但其值已更改,如下所示:
<?php
$nu = array_map( "strtoupper", $filtered);
var_dump($nu);
?>
现场代码在这里
这种情况下的代码使用内置的strtoupper()应用回调,但是用户定义的函数也是另一个可行的选择。回调适用于$ filtered的每个项目,从而产生$ nu,其元素包含大写值。
在下一个代码段中,数组walk()遍历$ nu并根据引用运算符'&'对每个元素进行更改。所做的更改不会创建其他数组。每个元素的值均会更改为更具信息性的字符串,以指定其键,类别和值。
<?php
$f = function(&$item,$key,$prefix) {
$item = "$key: $prefix: $item";
};
array_walk($nu, $f,"fruit");
var_dump($nu);
?>
观看演示
注意:关于array_walk()的回调函数采用两个参数,当由array_walk()调用时,它们也会自动获取元素的值及其键,并且该顺序也是如此。(在此处查看更多信息)。
$lambda
和$callback
只是现有函数的eta扩展,因此是完全多余的。您可以通过传递底层函数(的名称)来获得相同的结果:$filtered = array_filter($array, 'ctype_alpha');
和$nu = array_map('strtoupper', $filtered);