foreach和map有区别吗?

好吧,这更像是一个计算机科学问题,而不是一个基于特定语言的问题,但是map操作和foreach操作之间有区别吗?或者它们只是同一事物的不同名称?

80940 次浏览

不同。

foreach遍历列表,执行对每个列表成员执行一些有副作用的操作(例如将每个成员保存到数据库)

map遍历一个列表,转换该列表的每个成员,并返回另一个具有已转换成员的相同大小的列表(例如将字符串列表转换为大写)

它们之间的重要区别是map将所有结果累积到一个集合中,而foreach不返回任何结果。map通常用于想要用函数转换元素集合时,而foreach只是为每个元素执行一个操作。

如果你特别谈论Javascript,区别在于map是一个循环函数,而forEach是一个迭代器。

当你想对列表的每个成员应用操作并将结果作为一个新列表返回时,使用map,而不影响原始列表。

当你想在列表的每个元素的基础上某项时,使用forEach。例如,您可能正在向页面添加内容。从本质上讲,当你想要“副作用”时,它是很好的。

其他区别:forEach不返回任何东西(因为它实际上是一个控制流函数),传入函数获得对索引和整个列表的引用,而map返回新列表,只传入当前元素。

最“明显”的区别是map将结果累积到一个新的集合中,而foreach仅用于执行本身。

但这里有一些额外的假设:因为map的“目的”是新的值列表,所以执行顺序并不重要。事实上,一些执行环境会生成并行代码,甚至引入一些记忆来避免调用重复值,或者引入惰性来避免调用一些值。

另一方面,Foreach是专门用于副作用的;因此顺序很重要,而且通常不能并行。

简而言之,foreach用于对元素集合中的每个元素应用操作,而map用于将一个集合转换为另一个集合。

foreachmap之间有两个显著的区别。

  1. foreach对它应用的操作没有概念上的限制,除了可能接受一个元素作为参数。也就是说,操作可能什么也不做,可能有副作用,可能返回值,也可能不返回值。foreach所关心的是遍历元素集合,并对每个元素应用操作。

    另一方面,map确实对操作有限制:它期望操作返回一个元素,也可能接受一个元素作为参数。map操作遍历一个元素集合,对每个元素应用该操作,最后将每次操作调用的结果存储到另一个集合中。换句话说,map 转换一个集合变成另一个集合

  2. foreach工作于单个元素集合。这是输入集合。

    map用于两个元素集合:输入集合和输出集合

将这两种算法联系起来并不是错误的:事实上,你可以分层地看待这两种算法,其中mapforeach的专门化。也就是说,你可以使用foreach并让操作转换其参数并将其插入到另一个集合中。因此,foreach算法是map算法的抽象和泛化。事实上,因为foreach对其操作没有限制,我们可以安全地说foreach是最简单的循环机制,它可以做循环能做的任何事情。map以及其他更专业的算法是为了表达性而存在的:如果你希望将一个集合映射(或转换)到另一个集合,使用map比使用foreach的意图更明确。

我们可以进一步扩展这个讨论,并考虑copy算法:克隆集合的循环。这个算法也是foreach算法的专门化。您可以定义一个操作,给定一个元素,将该元素插入到另一个集合。如果你在该操作中使用foreach,你实际上执行了copy算法,尽管其清晰度、表达性或显式性有所降低。让我们更进一步:我们可以说mapcopy的专门化,它本身是foreach的专门化。map可以改变它遍历的任何元素。如果map没有改变任何元素,那么它只是foreach0元素,使用foreach1将更清楚地表达意图。

foreach算法本身可能有也可能没有返回值,这取决于语言。例如,在c++中,foreach返回它最初接收到的操作。其思想是,操作可能有一个状态,您可能希望返回该操作以检查它是如何在元素中演化的。map也可能返回值,也可能不返回值。在c++中,transform(这里等价于map)恰好返回一个指向输出容器(集合)末尾的迭代器。在Ruby中,map的返回值是输出序列(集合)。所以,算法的返回值实际上是一个实现细节;它们的效果可能是,也可能不是它们的回报。

Array.protototype.map方法&Array.protototype.forEach都非常相似。

运行如下代码:http://labs.codecademy.com/bw1/6#:workspace

var arr = [1, 2, 3, 4, 5];


arr.map(function(val, ind, arr){
console.log("arr[" + ind + "]: " + Math.pow(val,2));
});


console.log();


arr.forEach(function(val, ind, arr){
console.log("arr[" + ind + "]: " + Math.pow(val,2));
});

它们给出了完全相同的结果。

arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25


arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25

但当你运行以下代码时,扭转就来了:-

这里我只是简单地分配了map和forEach方法返回值的结果。

var arr = [1, 2, 3, 4, 5];


var ar1 = arr.map(function(val, ind, arr){
console.log("arr[" + ind + "]: " + Math.pow(val,2));
return val;
});


console.log();
console.log(ar1);
console.log();


var ar2 = arr.forEach(function(val, ind, arr){
console.log("arr[" + ind + "]: " + Math.pow(val,2));
return val;
});


console.log();
console.log(ar2);
console.log();

现在的结果有些棘手!

arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25


[ 1, 2, 3, 4, 5 ]


arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25


undefined

结论

Array.prototype.map返回一个数组,但Array.prototype.forEach不返回。因此,您可以在传递给map方法的回调函数中操作返回的数组,然后返回它。

Array.prototype.forEach只遍历给定的数组,所以你可以在遍历数组时做你的事情。

下面是一个Scala中使用列表的例子:map返回列表,foreach不返回任何内容。

def map(f: Int ⇒ Int): List[Int]
def foreach(f: Int ⇒ Unit): Unit

map返回函数f作用于每个列表元素后得到的列表:

scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)


scala> list map (x => x * 2)
res0: List[Int] = List(2, 4, 6)

Foreach只是对每个元素应用f:

scala> var sum = 0
sum: Int = 0


scala> list foreach (sum += _)


scala> sum
res2: Int = 6 // res1 is empty

简短的回答: mapforEach是不同的。另外,非正式地说,mapforEach的严格超集。

首先,让我们给出forEachmap的一行描述:

  • forEach遍历所有元素,在每个元素上调用所提供的函数。
  • map遍历所有元素,在每个元素上调用所提供的函数,并通过记住每个函数调用的结果来生成一个转换后的数组。

在许多语言中,forEach通常被称为each。下面的讨论使用JavaScript仅供参考。它真的可以是任何其他语言。

现在,让我们分别使用这些函数。

使用forEach:

写一个函数printSquares,它接受一个数字数组arr,并打印其中每个元素的平方。

解决方案1:

var printSquares = function (arr) {
arr.forEach(function (n) {
console.log(n * n);
});
};

使用map:

写一个函数selfDot,它接受数字数组arr,并返回一个数组,其中每个元素都是arr中相应元素的平方。

旁白:在这里,在俚语中,我们试图平方输入数组。正式地说,我们试着计算它与自身的点积。

解决方案2:

var selfDot = function (arr) {
return arr.map(function (n) {
return n * n;
});
};

map如何成为forEach的超集?

你可以使用map来解决这两个任务,任务1任务2。然而,你不能使用forEach来解决任务2

解决方案1中,如果你简单地将forEach替换为map,解决方案仍然有效。然而,在解决方案2中,用forEach替换map将破坏你之前的工作解决方案。

根据map实现forEach:

实现map优势的另一种方法是根据map实现forEach。由于我们是优秀的程序员,我们不会沉迷于名称空间污染。我们将forEach命名为each

Array.prototype.each = function (func) {
this.map(func);
};

现在,如果你不喜欢prototype的废话,在这里:

var each = function (arr, func) {
arr.map(func); // Or map(arr, func);
};

所以,嗯. .为什么forEach存在?

答案是效率。如果您对将一个数组转换为另一个数组不感兴趣,为什么要计算转换后的数组呢?只是为了把它扔掉?当然不是!如果你不想做变换,你就不应该做变换。

因此,虽然map可以用来解决任务1,它可能不应该。因为每个人都是正确的候选人。


最初的回答:

虽然我在很大程度上同意@madlep的回答,但我想指出,map()forEach()严格的超集

是的,map()通常用于创建一个新数组。但是,可以使用来更改当前数组。

这里有一个例子:

var a = [0, 1, 2, 3, 4], b = null;
b = a.map(function (x) { a[x] = 'What!!'; return x*x; });
console.log(b); // logs [0, 1, 4, 9, 16]
console.log(a); // logs ["What!!", "What!!", "What!!", "What!!", "What!!"]

在上面的例子中,a被方便地设置为i < a.lengtha[i] === i。即便如此,它也展示了map()的强大功能。

下面是map()的官方描述。注意map()甚至可以改变调用它的数组!冰雹map()

希望这对你有所帮助。


编辑2015年11月10日:增加详细说明。

ForEach尝试在RDD的每个元素上应用一个函数,比如写入db等,而不返回任何东西。

但是map()在rdd的元素上应用了一些函数并返回rdd。所以当你运行下面的方法时,它不会在第3行失败,但在应用foreach后收集rdd时,它会失败并抛出一个错误

文件“<stdin>"”,第5行,在<module>

AttributeError: 'NoneType'对象没有collect属性

nums = sc.parallelize([1,2,3,4,5,6,7,8,9,10])
num2 = nums.map(lambda x: x+2)
print ("num2",num2.collect())
num3 = nums.foreach(lambda x : x*x)
print ("num3",num3.collect())