わかりました、これは特定の言語に基づく質問ではなく、コンピューター サイエンスに関する質問ですが、map 操作と foreach 操作に違いはありますか? それとも、同じものの名前が違うだけですか?
9 に答える
違う。
foreach はリストを繰り返し処理し、各リスト メンバーに副作用のある操作を実行します (たとえば、各リスト メンバーをデータベースに保存するなど)。
map はリストを反復し、そのリストの各メンバーを変換し、変換されたメンバーと同じサイズの別のリストを返します (文字列のリストを大文字に変換するなど)。
それらの重要な違いは、map
すべての結果をコレクションに蓄積するのに対し、foreach
何も返さないことです。map
通常、関数を使用して要素のコレクションを変換する場合に使用されますが、要素foreach
ごとにアクションを実行するだけです。
つまり、foreach
要素のコレクションの各要素に操作を適用するためのものであり、あるmap
コレクションを別のコレクションに変換するためのものです。
と の間には 2 つの大きな違いがforeach
ありmap
ます。
foreach
おそらく要素を引数として受け入れることを除いて、適用される操作に概念的な制限はありません。つまり、操作は何もしないか、副作用があるか、値を返すか、値を返さない可能性があります。重要foreach
なのは、要素のコレクションを反復処理し、各要素に操作を適用することです。map
一方、操作には制限があります。操作が要素を返すことを期待しており、おそらく要素を引数として受け入れることもあります。操作は要素のmap
コレクションを反復処理し、各要素に操作を適用し、最後に操作の各呼び出しの結果を別のコレクションに格納します。つまり、 はあるコレクションを別のコレクションにmap
変換します。foreach
要素の単一のコレクションで動作します。これは入力コレクションです。map
入力コレクションと出力コレクションの 2 つの要素のコレクションで動作します。
map
2 つのアルゴリズムを関連付けることは間違いではありません。実際、2 つのアルゴリズムを階層的に見ることができます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
しかし、次のコードを実行すると、ねじれが生じます。-
ここでは、マップと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
与えられた配列を歩くだけなので、配列を歩きながら自分の仕事をすることができます。
最も「目に見える」違いは、マップが結果を新しいコレクションに蓄積するのに対し、foreachは実行自体に対してのみ実行されることです。
ただし、いくつかの追加の前提条件があります。マップの「目的」は新しい値のリストであるため、実行の順序は実際には重要ではありません。実際、実行環境によっては、並列コードを生成したり、メモ化を導入して繰り返し値の呼び出しを回避したり、怠惰を導入して、呼び出しをまったく回避したりします。
一方、foreachは、特に副作用のために呼び出されます。したがって、順序は重要であり、通常は並列化できません。
短い答え: map
とforEach
は異なります。また、非公式に言えば、map
は の厳密なスーパーセットですforEach
。
長い答え:forEach
まず、 と の 1 行の説明を考えてみましょうmap
:
forEach
すべての要素を繰り返し処理し、それぞれに対して指定された関数を呼び出します。map
すべての要素を反復処理し、それぞれに対して指定された関数を呼び出し、各関数呼び出しの結果を記憶して変換された配列を生成します。
多くの言語でforEach
は、単に と呼ばれることがよくありeach
ます。以下の説明では、JavaScript を参照用にのみ使用しています。それは本当に他の言語である可能性があります。
それでは、それぞれの機能を使ってみましょう。
使用forEach
:
タスク 1:printSquares
数値の配列を受け取る関数 を作成し、arr
その中の各要素の 2 乗を出力します。
解決策 1:
var printSquares = function (arr) {
arr.forEach(function (n) {
console.log(n * n);
});
};
使用map
:
タスク 2:selfDot
数値の配列を受け取り、arr
各要素が の対応する要素の 2 乗である配列を返す関数を作成しますarr
。
余談ですが、俗語で言えば、入力配列を 2 乗しようとしています。正式には、それ自体の内積を計算しようとしています。
解決策 2:
var selfDot = function (arr) {
return arr.map(function (n) {
return n * n;
});
};
map
のスーパーセットはどうforEach
ですか?
を使用して、タスク 1とタスク 2map
の両方のタスクを解決できます。ただし、 を使用してタスク 2を解決することはできません。forEach
解決策 1では、単純に に置き換えforEach
てmap
も、解決策は引き続き有効です。ただし、ソリューション 2では、に置き換えると、以前に機能map
しforEach
ていたソリューションが壊れます。
forEach
の観点からの実装map
:
map
の優位性を実現するもう 1 つの方法は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を解決できますが、おそらくそうすべきではありません。それぞれがそのための正しい候補です。
元の答え:
map()
私は@madlepの答えに大部分同意しますが、それが厳密なスーパーセットであることを指摘したいと思い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!!"]
上記の例では、 はfor の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 は新しいリストを返し、現在の要素のみを渡します。