คำตอบอื่น ๆ แสดงให้เห็นถึงความแตกต่างระหว่าง array_walk (การแก้ไขในสถานที่) และ array_map (กลับมาแก้ไขสำเนา) ค่อนข้างดี อย่างไรก็ตามพวกเขาไม่ได้กล่าวถึง array_reduce ซึ่งเป็นวิธีการส่องสว่างที่จะเข้าใจ array_map และ array_filter
ฟังก์ชั่น array_reduce ใช้อาร์เรย์ฟังก์ชั่นสองข้อโต้แย้งและ 'สะสม' เช่นนี้:
array_reduce(array('a', 'b', 'c', 'd'),
'my_function',
$accumulator)
องค์ประกอบของอาร์เรย์จะรวมกับตัวสะสมหนึ่งครั้งโดยใช้ฟังก์ชั่นที่กำหนด ผลลัพธ์ของการโทรด้านบนเหมือนกับการทำสิ่งนี้:
my_function(
my_function(
my_function(
my_function(
$accumulator,
'a'),
'b'),
'c'),
'd')
หากคุณต้องการที่จะคิดในแง่ของลูปมันก็เหมือนกับการทำสิ่งต่อไปนี้ (ฉันเคยใช้สิ่งนี้เป็นทางเลือกเมื่อ array_reduce ไม่พร้อมใช้งาน):
function array_reduce($array, $function, $accumulator) {
foreach ($array as $element) {
$accumulator = $function($accumulator, $element);
}
return $accumulator;
}
เวอร์ชันลูปนี้ทำให้ชัดเจนว่าทำไมฉันจึงเรียกอาร์กิวเมนต์ที่สามเป็น 'ตัวสะสม': เราสามารถใช้มันเพื่อสะสมผลลัพธ์ผ่านการวนซ้ำแต่ละครั้ง
ดังนั้นสิ่งนี้เกี่ยวข้องกับ array_map และ array_filter? ปรากฎว่าพวกเขาทั้งสองประเภทของ array_reduce เราสามารถใช้สิ่งเหล่านี้:
array_map($function, $array) === array_reduce($array, $MAP, array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())
ไม่สนใจข้อเท็จจริงที่ว่า array_map และ array_filter รับข้อโต้แย้งตามลำดับอื่น นั่นเป็นเพียงอีกหนึ่งการเล่นโวหารของ PHP จุดสำคัญคือด้านขวามือเหมือนกันยกเว้นฟังก์ชั่นที่ฉันเรียกว่า $ MAP และ $ FILTER แล้วพวกมันมีหน้าตาเป็นอย่างไร?
$MAP = function($accumulator, $element) {
$accumulator[] = $function($element);
return $accumulator;
};
$FILTER = function($accumulator, $element) {
if ($function($element)) $accumulator[] = $element;
return $accumulator;
};
อย่างที่คุณสามารถเห็นได้ฟังก์ชั่นทั้งสองใช้เวลาในการ $ สะสมและส่งกลับมาอีกครั้ง ฟังก์ชันเหล่านี้มีความแตกต่างกันสองประการ:
- $ MAP จะผนวกเข้ากับ $ accumulator เสมอ แต่ $ FILTER จะทำเช่นนั้นก็ต่อเมื่อฟังก์ชัน $ ($ องค์ประกอบ) เป็น TRUE
- $ FILTER ต่อท้ายองค์ประกอบดั้งเดิม แต่ $ MAP ผนวก $ function ($ องค์ประกอบ)
โปรดทราบว่านี่อยู่ไกลจากเรื่องไม่สำคัญไร้ประโยชน์ เราสามารถใช้มันเพื่อทำให้อัลกอริทึมของเรามีประสิทธิภาพมากขึ้น!
เรามักจะเห็นโค้ดเหมือนสองตัวอย่างนี้:
// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))
// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')
การใช้ array_map และ array_filter แทนการวนซ้ำทำให้ตัวอย่างเหล่านี้ดูค่อนข้างดี อย่างไรก็ตามมันจะไม่มีประสิทธิภาพมากถ้า $ inputs มีขนาดใหญ่เนื่องจากการโทรครั้งแรก (แผนที่หรือตัวกรอง) จะข้าม $ input และสร้างอาร์เรย์กลาง อาร์เรย์กลางนี้จะถูกส่งตรงไปยังการเรียกครั้งที่สองซึ่งจะย้อนกลับไปสู่สิ่งทั้งหมดอีกครั้งจากนั้นอาร์เรย์กลางจะต้องถูกรวบรวมขยะ
เราสามารถกำจัดอาร์เรย์กลางนี้ได้โดยใช้ประโยชน์จากข้อเท็จจริงที่ว่า array_map และ array_filter เป็นทั้งสองตัวอย่างของ array_reduce โดยการรวมเข้าด้วยกันเราจะต้องสำรวจ $ อินพุตหนึ่งครั้งในแต่ละตัวอย่าง:
// Transform valid inputs
array_reduce($inputs,
function($accumulator, $element) {
if (valid($element)) $accumulator[] = transform($element);
return $accumulator;
},
array())
// Get all numeric IDs
array_reduce($inputs,
function($accumulator, $element) {
$id = get_id($element);
if (is_numeric($id)) $accumulator[] = $id;
return $accumulator;
},
array())
หมายเหตุ: การใช้งานของฉันของ array_map และ array_filter ด้านบนจะไม่ทำงานเหมือนกับ PHP เพราะ array_map ของฉันสามารถจัดการได้ครั้งละหนึ่งอาร์เรย์เท่านั้นและ array_filter ของฉันจะไม่ใช้ "empty" เป็นฟังก์ชัน $ เริ่มต้น นอกจากนี้จะไม่รักษากุญแจ
ไม่ใช่เรื่องยากที่จะทำให้พวกเขาประพฤติตนเหมือน PHP แต่ฉันรู้สึกว่าภาวะแทรกซ้อนเหล่านี้จะทำให้แนวคิดหลักยากขึ้น