好吧,这更像是一个计算机科学问题,而不是基于特定语言的问题,但是 map 操作和 foreach 操作之间有区别吗?还是它们只是同一事物的不同名称?
9 回答
不同的。
foreach 迭代一个列表并对每个列表成员执行一些具有副作用的操作(例如将每个成员保存到数据库中)
map 遍历一个列表,转换该列表的每个成员,并返回另一个与转换后的成员大小相同的列表(例如将字符串列表转换为大写)
它们之间的重要区别是将map
所有结果累积到一个集合中,而foreach
什么也不返回。map
当您想使用函数转换元素集合时通常使用它,而foreach
只需为每个元素执行一个操作。
简而言之,foreach
用于对元素集合的每个元素应用操作,而map
用于将一个集合转换为另一个集合。
foreach
和之间有两个显着差异map
。
foreach
对其应用的操作没有概念上的限制,除了可能接受一个元素作为参数。也就是说,该操作可能什么都不做,可能有副作用,可能返回一个值,也可能不返回一个值。所有foreach
关心的是遍历元素集合,并对每个元素应用操作。map
另一方面,它确实对操作有限制:它期望操作返回一个元素,并且可能还接受一个元素作为参数。该map
操作遍历元素集合,对每个元素应用该操作,最后将每次调用该操作的结果存储到另一个集合中。换句话说,map
将一个集合转换为另一个集合。foreach
适用于单个元素集合。这是输入集合。map
使用两个元素集合:输入集合和输出集合。
将这两种算法联系起来并没有错:实际上,您可以分层查看这两种算法,map
其中foreach
. 也就是说,您可以使用foreach
并让操作转换其参数并将其插入另一个集合。因此,foreach
算法是算法的抽象、概括map
。事实上,因为foreach
它的操作没有限制,我们可以肯定地说这foreach
是最简单的循环机制,它可以做任何循环可以做的事情。map
以及其他更专业的算法是否具有表现力:如果您希望将一个集合映射(或转换)到另一个集合,map
那么使用foreach
.
我们可以进一步扩展这个讨论,并考虑copy
算法:一个克隆集合的循环。该算法也是该算法的一种特殊化foreach
。您可以定义一个操作,给定一个元素,将相同的元素插入另一个集合。如果您使用foreach
该操作,您实际上执行了copy
算法,尽管清晰度、表达性或明确性有所降低。让我们更进一步:我们可以说它map
是 的一个特化copy
,它本身就是 的一个特化foreach
。map
可能会更改它迭代的任何元素。如果map
不更改任何元素,则它只是复制元素,并使用复制会更清楚地表达意图。
算法本身可能有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
只遍历给定的数组,这样你就可以在遍历数组的同时做你的事情。
最“明显”的区别是 map 将结果累积到一个新的集合中,而 foreach 仅针对执行本身进行。
但是还有一些额外的假设:由于 map 的“目的”是新的值列表,因此执行顺序并不重要。事实上,一些执行环境会生成并行代码,甚至引入一些 memoizing 以避免调用重复值,或者引入惰性,以完全避免调用一些。
另一方面,foreach 是专门针对副作用而调用的;因此顺序很重要,通常不能并行化。
简短的回答: map
并且forEach
是不同的。此外,非正式地说,map
是 的严格超集forEach
。
长答案:forEach
首先,让我们对and进行一行描述map
:
forEach
遍历所有元素,在每个元素上调用提供的函数。map
遍历所有元素,在每个元素上调用提供的函数,并通过记住每个函数调用的结果来生成一个转换后的数组。
在许多语言中,forEach
通常称为 just each
。以下讨论仅使用 JavaScript 作为参考。它真的可以是任何其他语言。
现在,让我们使用这些功能中的每一个。
使用forEach
:
任务 1:编写一个函数printSquares
,它接受一个数字数组arr
,并打印其中每个元素的平方。
解决方案1:
var printSquares = function (arr) {
arr.forEach(function (n) {
console.log(n * n);
});
};
使用map
:
任务 2:编写一个函数selfDot
,它接受一个数字数组arr
,并返回一个数组,其中每个元素都是 中对应元素的平方arr
。
旁白:在这里,用俚语来说,我们正在尝试对输入数组进行平方。正式地说,我们正在尝试计算它与自身的点积。
解决方案2:
var selfDot = function (arr) {
return arr.map(function (n) {
return n * n;
});
};
map
的超集是怎样的forEach
?
您可以使用map
来解决两个任务Task 1和Task 2。但是,您不能用于forEach
解决Task 2。
在解决方案 1中,如果您简单地替换forEach
为map
,该解决方案仍然有效。但是,在解决方案 2中,替换map
为forEach
会破坏您以前工作的解决方案。
实施forEach
方面map
:
实现优势的另一种方式map
是在实施forEach
方面map
。由于我们是优秀的程序员,我们不会沉迷于命名空间污染。我们会打电话给我们的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 可用于解决Task 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
方便地设置a[i] === i
为i < a.length
. 即便如此,它也展示了map()
.
下面是官方的介绍map()
。请注意,map()
甚至可能会更改调用它的数组!冰雹map()
。
希望这有帮助。
2015 年 11 月 10 日编辑:添加了详细说明。
这是 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
如果您特别谈论 Javascript,则不同之处在于它map
是一个循环函数,而forEach
它是一个迭代器。
map
当您想要对列表的每个成员应用操作并将结果作为新列表返回时使用,而不影响原始列表。
forEach
当您想根据列表的每个元素做某事时使用。例如,您可能正在向页面添加内容。本质上,当您想要“副作用”时,它非常有用。
其他区别:forEach
什么都不返回(因为它实际上是一个控制流函数),并且传入的函数获取对索引和整个列表的引用,而 map 返回新列表并且只传入当前元素。
ForEach 尝试在 RDD 的每个元素上应用一个函数,例如写入 db 等,而不返回任何内容。
但是在map()
rdd 的元素上应用了一些函数并返回 rdd。因此,当您运行以下方法时,它不会在第 3 行失败,但是在应用 foreach 后收集 rdd 时,它将失败并抛出一个错误,上面写着
<module> 中的文件“<stdin>”,第 5 行
AttributeError:“NoneType”对象没有“收集”属性
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())