มีความแตกต่างระหว่าง foreach และแผนที่หรือไม่?


218

ตกลงนี่เป็นคำถามเกี่ยวกับวิทยาศาสตร์คอมพิวเตอร์มากกว่าคำถามที่ใช้ภาษาใดภาษาหนึ่ง แต่มีความแตกต่างระหว่างการทำแผนที่และการดำเนินการ foreach หรือไม่? หรือพวกเขาเป็นชื่อที่แตกต่างกันสำหรับสิ่งเดียวกันหรือไม่


อยากรู้อยากเห็นถ้าฉันได้รับIterator[String]จากscala.io.Source.fromFile("/home/me/file").getLines()และนำ.foreach(s => ptintln(s))ไปใช้กับมันมันก็โอเค แต่ว่างเปล่าหลังจากนั้น ในเวลาเดียวกันถ้าฉันสมัคร.map(ptintln(_))- มันว่างเปล่าและไม่มีอะไรพิมพ์
Ivan

คำตอบ:


294

ต่าง

foreach วนซ้ำทุกรายการและใช้การดำเนินการบางอย่างที่มีผลข้างเคียงกับสมาชิกแต่ละราย (เช่นบันทึกแต่ละรายการในฐานข้อมูลเป็นต้น)

แมปซ้ำทุกรายการเปลี่ยนสมาชิกแต่ละรายของรายการนั้นและส่งกลับรายการขนาดเดียวกันอีกครั้งด้วยสมาชิกที่ถูกเปลี่ยนรูป (เช่นการแปลงรายการสตริงเป็นตัวพิมพ์ใหญ่)


ขอบคุณฉันคิดว่ามันมีอะไรบางอย่างตามสายเหล่านี้ แต่ไม่แน่ใจ!
Robert Gould

19
แต่คุณอาจมีผลข้างเคียงเกิดขึ้นในฟังก์ชั่นแผนที่ได้เช่นกันฉัน
TinyTimZamboni

6
จุดหนึ่งที่สำคัญที่จะต้องพูดถึง (นี้เป็นจริงโดยเฉพาะอย่างยิ่งใน Scala) เป็นที่เรียกร้องให้mapไม่ได้ผลในการดำเนินการของตรรกะพื้นฐานของจนเมื่อคาดว่ารายการเปลี่ยนถูกเรียก ในทางตรงกันข้ามforeachการดำเนินการของคำนวณทันที
bigT

124

ความแตกต่างที่สำคัญระหว่างพวกเขาคือการmapรวบรวมผลลัพธ์ทั้งหมดลงในคอลเลกชันในขณะที่foreachไม่มีอะไรคืน mapโดยปกติจะใช้เมื่อคุณต้องการแปลงชุดองค์ประกอบด้วยฟังก์ชันในขณะที่foreachเพียงเรียกใช้การกระทำสำหรับแต่ละองค์ประกอบ


ขอบคุณ! ตอนนี้ฉันเข้าใจความแตกต่าง มันมืดมนสำหรับฉันมาระยะหนึ่งแล้ว
Robert Gould ใน

สำคัญมาก!
МатиТернер

40

กล่าวโดยย่อforeachคือการใช้การดำเนินการกับแต่ละองค์ประกอบของการรวบรวมองค์ประกอบในขณะที่mapสำหรับการเปลี่ยนคอลเลกชันหนึ่งเป็นคอลเลกชันอื่น

มีสองความแตกต่างที่สำคัญระหว่างมีและforeachmap

  1. foreachไม่มีข้อ จำกัด ทางแนวคิดเกี่ยวกับการดำเนินการที่ใช้นอกเหนือจากอาจยอมรับองค์ประกอบเป็นอาร์กิวเมนต์ นั่นคือการดำเนินการอาจไม่ทำอะไรเลยอาจมีผลข้างเคียงอาจส่งคืนค่าหรืออาจไม่ส่งคืนค่า สิ่งที่ทุกคนforeachใส่ใจคือทำซ้ำมากกว่าชุดขององค์ประกอบและใช้การดำเนินการกับแต่ละองค์ประกอบ

    mapในทางกลับกันมีข้อ จำกัด ในการดำเนินการ: คาดว่าการดำเนินการเพื่อส่งคืนองค์ประกอบและอาจยอมรับองค์ประกอบเป็นอาร์กิวเมนต์ การmapดำเนินการวนซ้ำในการรวบรวมองค์ประกอบการใช้การดำเนินการกับแต่ละองค์ประกอบและในที่สุดก็เก็บผลลัพธ์ของการเรียกใช้การดำเนินการแต่ละครั้งลงในคอลเล็กชันอื่น กล่าวอีกนัยหนึ่งเป็นการmap แปลงคอลเล็กชันหนึ่งเป็นคอลเล็กชั่นอื่น

  2. foreachทำงานร่วมกับองค์ประกอบชุดเดียว นี่คือการรวบรวมอินพุต

    map ทำงานร่วมกับองค์ประกอบสองชุด: การรวบรวมข้อมูลเข้าและการรวบรวมผลลัพธ์

มันไม่ได้เป็นความผิดพลาดที่จะเกี่ยวข้องกับทั้งสองขั้นตอนวิธีการ: ในความเป็นจริงคุณอาจดูสองลำดับชั้นที่เป็นความเชี่ยวชาญของmap foreachนั่นคือคุณสามารถใช้foreachและให้การดำเนินการแปลงอาร์กิวเมนต์แล้วใส่ลงในคอลเล็กชันอื่น ดังนั้นforeachอัลกอริธึมจึงเป็นนามธรรมการวางนัยของmapอัลกอริทึม ในความเป็นจริงเนื่องจากforeachไม่มีข้อ จำกัด ในการใช้งานเราสามารถพูดได้อย่างปลอดภัยว่าforeachเป็นกลไกการวนรอบที่ง่ายที่สุดที่นั่นและสามารถทำทุกอย่างที่ลูปสามารถทำได้ mapเช่นเดียวกับขั้นตอนวิธีพิเศษอื่น ๆ จะมีความหมายสำหรับ: ถ้าคุณต้องการที่จะ map (หรือเปลี่ยน) หนึ่งในคอลเลกชันลงในอีกความตั้งใจของคุณเป็นที่ชัดเจนถ้าคุณใช้กว่าถ้าคุณใช้mapforeach

เราสามารถขยายการอภิปรายนี้เพิ่มเติมและพิจารณาcopyอัลกอริทึม: ลูปที่โคลนโคลนคอลเลกชัน อัลกอริทึมนี้ก็เป็นความเชี่ยวชาญของforeachอัลกอริทึม คุณสามารถกำหนดการดำเนินการที่กำหนดองค์ประกอบจะแทรกองค์ประกอบเดียวกันนั้นลงในคอลเลกชันอื่น หากคุณใช้foreachกับการดำเนินการนั้นคุณจะได้ใช้copyอัลกอริทึมแม้ว่าจะมีความชัดเจนลดลงแสดงออกหรือชัดเจน เราจะทำมันให้ดียิ่งขึ้น: เราสามารถพูดได้ว่าmapเป็นความเชี่ยวชาญของตัวเองความเชี่ยวชาญของcopy อาจเปลี่ยนองค์ประกอบใด ๆ ที่ทำซ้ำได้ หากไม่เปลี่ยนองค์ประกอบใด ๆ ก็เพียงแค่คัดลอกองค์ประกอบและการใช้งานforeachmapmapคัดลอก จะแสดงเจตนาที่ชัดเจนยิ่งขึ้น

foreachอัลกอริทึมตัวเองอาจหรือไม่อาจมีค่าตอบแทนขึ้นอยู่กับภาษา ใน C ++ ตัวอย่างเช่นforeachส่งคืนการดำเนินการที่ได้รับมาตั้งแต่แรก แนวคิดคือการดำเนินการอาจมีสถานะและคุณอาจต้องการให้การดำเนินการนั้นกลับไปตรวจสอบว่ามีการพัฒนาไปอย่างไรกับองค์ประกอบต่างๆ mapเช่นกันอาจจะหรืออาจจะไม่คืนค่า ใน C ++ transform(เทียบเท่าสำหรับmapที่นี่) เกิดขึ้นเพื่อส่งกลับตัววนซ้ำไปยังจุดสิ้นสุดของคอนเทนเนอร์ส่งออก (คอลเลกชัน) ใน 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ไม่ ดังนั้นคุณสามารถจัดการอาเรย์ที่ส่งคืนภายในฟังก์ชันการเรียกกลับที่ส่งผ่านไปยังวิธีการแมปแล้วส่งคืน

Array.prototype.forEach เดินผ่านอาร์เรย์ที่กำหนดเท่านั้นเพื่อให้คุณสามารถทำสิ่งต่าง ๆ ของคุณในขณะที่เดินอาร์เรย์


11

ความแตกต่างที่ 'มองเห็นได้' มากที่สุดคือแผนที่จะรวบรวมผลลัพธ์ในคอลเลกชันใหม่ในขณะที่ foreach จะทำเพื่อการประมวลผลเท่านั้น

แต่มีข้อสมมติเพิ่มเติมสองสามข้อ: เนื่องจาก 'จุดประสงค์' ของแผนที่คือรายการค่าใหม่มันไม่สำคัญกับลำดับของการดำเนินการ ในความเป็นจริงบางสภาพแวดล้อมการดำเนินการสร้างรหัสขนานหรือแม้กระทั่งแนะนำการบันทึกความจำบางอย่างเพื่อหลีกเลี่ยงการเรียกค่าซ้ำหรือขี้เกียจเพื่อหลีกเลี่ยงการโทรบางอย่างเลย

foreach ตรงกันข้ามถูกเรียกโดยเฉพาะสำหรับผลข้างเคียง; ดังนั้นคำสั่งซื้อจึงมีความสำคัญและมักไม่สามารถเทียบเคียงได้


11

คำตอบสั้น ๆ : mapและforEachแตกต่างกัน นอกจากนี้ทางการพูดmapเป็น superset forEachที่เข้มงวดของ

คำตอบยาว:อันดับแรกให้เกิดขึ้นกับหนึ่งในรายละเอียดของเส้นforEachและmap:

  • forEach วนซ้ำองค์ประกอบทั้งหมดเรียกใช้ฟังก์ชันที่ให้มาในแต่ละองค์ประกอบ
  • map วนซ้ำองค์ประกอบทั้งหมดเรียกใช้ฟังก์ชันที่ให้มาในแต่ละรายการและสร้างอาร์เรย์ที่แปลงรูปโดยการจดจำผลลัพธ์ของการเรียกใช้ฟังก์ชันแต่ละครั้ง

ในหลายภาษา forEacheachมักจะเรียกว่าเพียงแค่ การสนทนาต่อไปนี้ใช้ 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เพื่อแก้ปัญหางานทั้งสองงาน 1และที่ 2 อย่างไรก็ตามคุณไม่สามารถใช้forEachเพื่อแก้ไขภารกิจที่ 22

ในโซลูชันที่ 1ถ้าคุณเพียงแค่แทนที่forEachด้วยmapโซลูชันจะยังคงใช้ได้ อย่างไรก็ตามในโซลูชันที่ 2 การแทนที่mapด้วยforEachจะทำให้โซลูชันที่คุณใช้ก่อนหน้านี้ทำงานไม่สมบูรณ์

ดำเนินการforEachในแง่ของmap:

วิธีการของการตระหนักถึงอีกmap's ที่เหนือกว่าคือการใช้ในแง่ของforEach mapเนื่องจากเราเป็นโปรแกรมเมอร์ที่ดีเราจะไม่หลงระเริงกับมลภาวะเนมสเปซ เราจะเรียกเราเพียงforEacheach

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

ตอนนี้ถ้าคุณไม่ชอบprototypeเรื่องไร้สาระที่นี่คุณไป:

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

อืมม .. ทำไม forEachถึงมีอยู่จริง?

คำตอบคือประสิทธิภาพ หากคุณไม่สนใจที่จะแปลงอาเรย์เป็นอีกอาเรย์ทำไมคุณต้องคำนวณอาเรย์ที่ถูกแปลง? เท่านั้นที่จะทิ้งมันได้หรือไม่ ไม่แน่นอน! หากคุณไม่ต้องการการแปลงคุณไม่ควรทำการแปลง

ดังนั้นในขณะที่แผนที่สามารถใช้แก้ปัญหาภารกิจที่ 1 ได้แต่ก็ไม่ควร สำหรับแต่ละผู้สมัครที่เหมาะสมสำหรับการที่


คำตอบเดิม:

ในขณะที่ผมส่วนใหญ่เห็นด้วยกับ @madlep 's คำตอบผมอยากจะชี้ให้เห็นว่า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()ลูกเห็บ

หวังว่านี่จะช่วยได้


แก้ไข 10 พ.ย. 2558: เพิ่มรายละเอียด


-1 เนื่องจากจะใช้ได้เฉพาะใน javascript เท่านั้น ในขณะที่คำถามพูดถึงเกี่ยวกับแนวคิด agnosticy ภาษาและวิทยาศาสตร์คอมพิวเตอร์ไม่ จำกัด การใช้งาน
Javier

1
@Javier: อืม .. ฉันต้องเห็นด้วยกับคุณเกี่ยวกับคำตอบของฉันว่าเฉพาะ JavaScript แต่ถามตัวเองนี้: ถ้าเป็นภาษาพื้นเมืองมีmapฟังก์ชั่น แต่ไม่มีforEach; คุณไม่สามารถใช้mapแทนได้forEachใช่ไหม ด้านพลิกถ้าภาษาที่มีforEachแต่ไม่มีคุณต้องการมีการดำเนินการของคุณเองmap mapคุณอาจจะไม่ได้เพียงแค่ใช้แทนforEach mapบอกสิ่งที่คุณคิดว่า.
Sumukh Barve

3

นี่คือตัวอย่างใน Scala ที่ใช้ list: map return list, 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ไม่ส่งคืนสิ่งใด (เนื่องจากเป็นฟังก์ชันโฟลว์ควบคุม) และฟังก์ชัน pass-in ได้รับการอ้างอิงไปยังดัชนีและรายการทั้งหมดในขณะที่ map ส่งคืนรายการใหม่และส่งผ่านเฉพาะในองค์ประกอบปัจจุบันเท่านั้น


0

ForEach พยายามที่จะใช้ฟังก์ชั่นเช่นการเขียนไปยัง db ฯลฯ ในแต่ละองค์ประกอบของ RDD โดยไม่คืนสิ่งใดกลับมา

แต่การmap()ใช้ฟังก์ชั่นบางอย่างมากกว่าองค์ประกอบของ rdd และส่งกลับ rdd ดังนั้นเมื่อคุณเรียกใช้วิธีการด้านล่างมันจะไม่ล้มเหลวที่ line3 แต่ในขณะที่รวบรวม rdd หลังจากใช้ foreach มันจะล้มเหลวและโยนข้อผิดพลาดที่ระบุว่า

ไฟล์ "<stdin>" บรรทัด 5 ใน <module>

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())
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.