foreachとmapの間に違いはありますか?


218

これは、特定の言語に基づく質問ではなく、コンピュータサイエンスの質問ですが、マップ操作とforeach操作の間に違いはありますか?それとも、同じものの名前が違うだけですか?


不思議なことに、私がIterator[String]from を取得しscala.io.Source.fromFile("/home/me/file").getLines()て適用.foreach(s => ptintln(s))した場合、それは問題なく出力されますが、直後は空になります。同時に私.map(ptintln(_))がそれに適用する場合-それは単に空になり、何も印刷されません。
Ivan

回答:


294

違う。

foreachはリストを反復処理し、副作用のある操作を各リストメンバーに適用します(それぞれをデータベースに保存するなど)

mapはリストを反復処理し、そのリストの各メンバーを変換し、変換されたメンバーと同じサイズの別のリストを返します(文字列のリストを大文字に変換するなど)


ありがとう、私はそれがこれらの線に沿ったものだと思っていましたが、よくわかりませんでした!
ロバートグールド

19
しかし、map関数でも副作用が発生する可能性があります。私はこの答えが好きです:stackoverflow.com/a/4927981/375688
TinyTimZamboni

6
言及すべき重要な点の1つ(これは、Scalaで特に当てはまります)への呼び出しmapは、予期される変換済みリストが呼び出されるまで、その基礎となるロジックの実行にはなりませ。対照的に、foreachの操作はすぐに計算されます。
bigT 2016

124

それらの間の重要な違いは、mapすべての結果をコレクションに蓄積する一方で、foreach何も返さないことです。map通常は、要素のコレクションを関数で変換するときに使用しますforeachが、各要素に対してアクションを実行するだけです。


ありがとう!今私は違いを理解しています。それはかなり長い間ぼんやりしていた
ロバート・グールド

また、非常に重要です!
МатиТернер

40

つまり、foreachは要素のコレクションの各要素に操作を適用するためのものであり、一方のmapコレクションを別のコレクションに変換するためのものです。

との間には2つの大きな違いがforeachありmapます。

  1. foreach要素を引数として受け入れる以外は、適用される操作に概念的な制限はありません。つまり、操作は何も実行しない、副作用がある、値を返す、または値を返さない場合があります。すべてforeach気には要素のコレクションを反復処理し、各要素に対して操作を適用することです。

    map一方、操作には制限があります。操作は要素を返すことを期待しており、おそらく要素を引数として受け入れます。map動作は、各要素に対して操作を適用して、要素のコレクションにわたって反復し、そして最後に別のコレクションに操作の各呼び出しの結果を格納します。つまり、あるコレクションを別のコレクションにmap 変換します。

  2. foreach要素の単一のコレクションで動作します。これは入力コレクションです。

    map 要素の2つのコレクション(入力コレクションと出力コレクション)を操作します。

2つのアルゴリズムを関連付けることは間違いではありません。実際には、2つのアルゴリズムを階層的に表示できます。mapはの特殊化ですforeach。つまりforeach、引数を変換して使用し、別のコレクションに挿入することができます。したがって、foreachアルゴリズムは、アルゴリズムの抽象化、一般化mapです。実際、foreachその操作に制限がないため、これがforeach最も単純なループメカニズムであり、ループが実行できるすべてのことを実行できます。map、および他のより特殊なアルゴリズムは、表現力のためにあります。あるコレクションを別のコレクションにマップ(または変換)したい場合、を使用するmap場合よりも使用する場合の方が明確ですforeach

この議論をさらに拡張して、copyアルゴリズムを考えます。コレクションを複製するループです。このアルゴリズムもアルゴリズムの特殊化foreachです。要素を指定して、その同じ要素を別のコレクションに挿入する操作を定義できます。foreachその操作で使用する場合copy、明快さ、表現力、または明示性は低下しますが、実際にはアルゴリズムを実行しました。さらに考えてみましょう。これmapはの特殊化でありcopy、それ自体がの特殊化ですforeach。反復する要素を変更するmap可能性がありますmap要素を変更しない場合は、単に要素をコピーしコピーを 意図をより明確に表現します。

foreachアルゴリズム自体はや言語に応じて、戻り値があってもなくてもよいです。たとえばC ++では、foreach最初に受け取った操作を返します。アイデアは、操作に状態がある可能性があるということであり、その操作を元に戻して、要素上でどのように展開されたかを調べることができます。mapも、値を返す場合と返さない場合があります。C ++ transformmapここでは同等)では、出力コンテナー(コレクション)の最後にイテレーターを返します。Rubyでは、の戻り値mapは出力シーケンス(コレクション)です。したがって、アルゴリズムの戻り値は実際には実装の詳細です。それらの効果は、それらが返すものである場合とそうでない場合があります。


方法の例については.forEach()実装するために使用することができ.map()、ここを参照してください:stackoverflow.com/a/39159854/1524693
Chinoto Vokro

32

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

しかし、次のコードを実行するとツイストが発生します。

ここでは、マップと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 与えられた配列のみをウォークスルーするので、配列をウォークしながら何かを行うことができます。


11

最も「目に見える」違いは、mapが結果を新しいコレクションに蓄積するのに対し、foreachは実行自体に対してのみ行われることです。

ただし、追加の前提がいくつかあります。マップの「目的」は新しい値のリストであるため、実行の順序は重要ではありません。実際、一部の実行環境は並列コードを生成するか、繰り返し値の呼び出しを回避するためのメモ化を導入したり、まったく呼び出しを回避するために遅延を発生させたりします。

一方、foreachは特に副作用のために呼び出されます。したがって、順序は重要であり、通常は並列化できません。


11

短い答え: mapforEachは異なります。また、非公式に言えば、mapの厳密なスーパーセットですforEach

長い答え:まず、聞かせてのはの1行の説明を思い付く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乗しようとしています。正式に言えば、それ自体のドット積を計算しようとしています。

解決策2:

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

mapスーパーセットはforEachどうですか?

を使用mapして、タスク1タスク2の両方のタスクを解決できます。ただし、を使用forEachしてタスク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存在するのですか?

答えは効率です。配列を別の配列に変換することに関心がない場合、変換された配列を計算する必要があるのはなぜですか?それを捨てるだけですか?もちろん違います!変換が必要ない場合は、変換を実行しないでください。

したがって、マップはタスク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()ます。

の公式説明は次のとおりですmap()map()それが呼ばれる配列を変更するかもしれないことに注意してください!雹map()

これがお役に立てば幸いです。


2015年11月10日編集:エラボレーションを追加。


-1。これはJavaScriptでのみ有効であるため。質問は言語の不可知論とコンピューターサイエンスの概念について具体的に話しているが、実装は限定されていない。
ハビエル

1
@Javier:うーん..、私は私の答えがJavaScript固有であることについてあなたに同意する必要があります。しかし、これを自問してみてください:言語にネイティブmap関数があったが、ない場合forEachmap代わりに使用できませんでしたforEachか?逆に言えば、ある言語にがありforEach、ないmap場合は、独自のを実装する必要がありますmap。のforEach代わりに単純に使用することはできませんmap。あなたの考えを教えてください。
Sumukh Barve 2015

3

以下は、リストを使用した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

2

特にJavascriptについて話している場合、違いはmapループ関数ですが、forEachがイテレータであるということです。

mapリストの各メンバーに操作を適用し、元のリストに影響を与えずに結果を新しいリストとして取得する場合に使用します。

リストの各要素に基づい何かforEach実行したい場合に使用します。たとえば、ページに何かを追加する場合があります。基本的に、「副作用」が必要な場合に最適です。

その他の違い:(forEach実際には制御フロー関数であるため)何も返しません。渡された関数はインデックスとリスト全体への参照を取得しますが、mapは新しいリストを返し、現在の要素のみを渡します。


0

ForEachは、何も返さずに、RDDの各要素にdbなどへの書き込みなどの関数を適用しようとします。

しかし、map()はrddの要素に対していくつかの関数を適用し、rddを返します。したがって、以下のメソッドを実行すると、line3では失敗しませんが、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())
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.