foreach 和 map 有区别吗?

Posted

技术标签:

【中文标题】foreach 和 map 有区别吗?【英文标题】:Is there a difference between foreach and map? 【发布时间】:2010-09-26 04:14:04 【问题描述】:

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

【问题讨论】:

奇怪的是,如果我从scala.io.Source.fromFile("/home/me/file").getLines() 得到一个Iterator[String] 并将.foreach(s => ptintln(s)) 应用到它,它会打印好,但之后会变空。同时,如果我对它应用.map(ptintln(_)) - 它只是变空并且没有打印任何内容。 【参考方案1】:

不同。

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

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

【讨论】:

谢谢,我以为是这样的,但不确定! 但您也可能在地图功能中发生副作用。我喜欢这个答案:***.com/a/4927981/375688 有一点需要提一下(在 Scala 中尤其如此),对 map 的调用确实不会导致其底层逻辑的执行直到预期的转换后的列表被召唤。相比之下,foreach 的运算是立即计算的。【参考方案2】:

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

【讨论】:

谢谢!现在我明白了其中的区别。很长一段时间以来,我一直很迷茫【参考方案3】:

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

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

当您想根据列表的每个元素某事时,请使用forEach。例如,您可能正在向页面添加内容。本质上,当您需要“副作用”时,它非常有用。

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

【讨论】:

【参考方案4】:

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

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

另一方面,foreach 是专门针对副作用调用的;因此顺序很重要,通常不能并行化。

【讨论】:

【参考方案5】:

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

foreachmap 之间有两个显着差异。

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

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

    foreach 使用单个元素集合。这是输入集合。

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

将这两种算法联系起来并没有错:实际上,您可以分层查看这两种算法,其中mapforeach 的特化。也就是说,您可以使用foreach 并让操作转换其参数并将其插入另一个集合。因此,foreach 算法是map 算法的抽象、概括。事实上,因为foreach 对其操作没有限制,我们可以肯定地说foreach 是最简单的循环机制,它可以做任何循环可以做的事情。 map 以及其他更专业的算法是否具有表现力:如果您希望将一个集合映射(或转换)到另一个集合,则使用 map 比使用 foreach 时您的意图更清晰。

我们可以进一步扩展这个讨论,并考虑copy 算法:一个克隆集合的循环。该算法也是foreach 算法的特化。您可以定义一个操作,给定一个元素,将相同的元素插入另一个集合。如果您在该操作中使用foreach,则您实际上执行了copy 算法,尽管清晰度、表达性或明确性有所降低。让我们更进一步:我们可以说mapcopy 的特化,它本身就是foreach 的特化。 map 可以更改它迭代的任何元素。如果map 没有更改任何元素,那么它只是复制元素,使用copy会更清楚地表达意图。

foreach 算法本身可能有也可能没有返回值,具体取决于语言。例如,在 C++ 中,foreach 返回它最初收到的操作。这个想法是操作可能有一个状态,并且您可能希望该操作返回以检查它是如何在元素上演变的。 map 也可能返回值,也可能不返回值。在 C++ 中,transform(此处为 map 的等价物)碰巧将迭代器返回到输出容器(集合)的末尾。在 Ruby 中,map 的返回值是输出序列(集合)。因此,算法的返回值实际上是一个实现细节;它们的效果可能是也可能不是它们返回的效果。

【讨论】:

有关如何使用.forEach() 实现.map() 的示例,请参见此处:***.com/a/39159854/1524693【参考方案6】:

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 只遍历给定的数组,因此您可以在遍历数组的同时做自己的事情。

【讨论】:

【参考方案7】:

这是 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

【讨论】:

【参考方案8】:

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

长答案:首先,让我们想出forEachmap的一行描述:

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

在许多语言中,forEach 通常被称为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 1Task 2 这两个任务。但是,您不能使用forEach 来解决Task 2

解决方案1中,如果您只是将forEach 替换为map,解决方案仍然有效。但是,在解决方案 2 中,将 map 替换为 forEach 会破坏您之前工作的解决方案。

根据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 甚至存在?

答案是效率。如果您对将一个数组转换为另一个数组不感兴趣,为什么要计算转换后的数组?只是为了倾倒吗?当然不是!如果您不想进行转换,则不应该进行转换。

因此,虽然地图可用于解决任务 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] === ii < a.length。即便如此,它也展示了map() 的力量。

Here's the official description of map()。请注意,map() 甚至可能会更改调用它的数组!欢呼map()

希望这会有所帮助。


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

【讨论】:

-1,因为这只在javascript下有效;而这个问题专门讨论了语言不可知论和计算机科学概念,而不是限制实现。 @Javier:嗯..,关于我的回答是针对 JavaScript 的,我必须同意你的看法。但是问问自己这个问题:如果一种语言有原生的map 函数但没有forEach;你不能不使用map 而不是forEach 吗?另一方面,如果一种语言有forEach 但没有map,则您必须实现自己的map。您不能简单地使用forEach 代替map。告诉我你的想法。【参考方案9】:

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

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

文件“”,第 5 行,在

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())

【讨论】:

以上是关于foreach 和 map 有区别吗?的主要内容,如果未能解决你的问题,请参考以下文章

foreach与for的区别

foreach和for的区别

JS的forEach和map方法的区别,还有一个$each

.map、.every 和 .forEach 之间有啥区别?

我可以用foreach遍历两个大小相等的循环吗? [复制]

map()和forEach()的区别和理解