Lambdas เป็นฟังก์ชันที่ไม่ระบุชื่อ (ฟังก์ชันที่ไม่มีชื่อ) ที่สามารถกำหนดและใช้งานแบบอินไลน์ใน C++ โดยให้วิธีที่สะดวกในการส่งโค้ดเล็กๆ เป็นอาร์กิวเมนต์ไปยังฟังก์ชันอื่นๆ หรือเพื่อกำหนดฟังก์ชันที่มีอยู่โดยไม่ต้องสร้างฟังก์ชันที่มีชื่อ
Lambdas มอบวิธีที่ยืดหยุ่นและกระชับในการเขียนออบเจ็กต์คล้ายฟังก์ชันใน C++ และมีการใช้กันอย่างแพร่หลายในการเขียนโปรแกรม C++ สมัยใหม่
Lambdas ถูกกำหนดโดยใช้ไวยากรณ์ต่อไปนี้:
[ capture list ] ( argument list ) -> return type { function body }
capture list
ใช้เพื่อระบุตัวแปรจากขอบเขตโดยรอบที่จะสามารถเข้าถึงได้ภายในแลมบ์ดา คุณสามารถบันทึกตัวแปรตามค่า โดยการอ้างอิง หรือใช้this
argument list
ระบุพารามิเตอร์ที่จะส่งผ่านไปยังแลมบ์ดาreturn type
ระบุประเภทของค่าที่ lambda จะส่งกลับ หากไม่ได้ระบุไว้ คอมไพลเลอร์จะพยายามสรุปfunction body
ระบุรหัสที่จะดำเนินการเมื่อมีการเรียกแลมบ์ดา
ต่อไปนี้เป็นวิธีต่างๆ บางประการในการใช้ lambdas ใน C++:
- ฟังก์ชั่นการโทรกลับ
- กำลังบันทึกค่าเริ่มต้น
- การจับตามมูลค่า
- การจับโดยการอ้างอิง
- แลมบ์ดาที่ไม่แน่นอน
{1} การเรียกกลับฟังก์ชัน
ฟังก์ชั่นการเรียกกลับเป็นฟังก์ชันที่ถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังฟังก์ชันอื่นและถูกเรียกโดยฟังก์ชันรับในภายหลัง คุณสามารถส่งแลมบ์ดาเป็นอาร์กิวเมนต์ของฟังก์ชันได้ โดยที่มันจะถูกดำเนินการเมื่อมีเหตุการณ์บางอย่างเกิดขึ้น
ตัวอย่าง:
#include <iostream> #include <algorithm> #include <vector> int main() { std::vector<int> numbers = {1, 2, 3, 4, 5}; // Lambda expression to find the sum of two numbers auto sum = [](int a, int b) { return a + b; }; int result = std::accumulate(numbers.begin(), numbers.end(), 0, sum); std::cout << "Sum of elements in the vector: " << result << std::endl; return 0; }
ในตัวอย่างนี้ ตัวแปร sum
คือนิพจน์ Lambda ที่รับพารามิเตอร์ 2 ตัว a
และ b
แล้วส่งคืนผลรวม ฟังก์ชัน std::accumulate
รับเวกเตอร์ของตัวเลข ค่าเริ่มต้นของผลลัพธ์ และฟังก์ชันผลรวม (นิพจน์ Lambda) ฟังก์ชันจะคำนวณผลรวมขององค์ประกอบทั้งหมดในเวกเตอร์และส่งกลับผลลัพธ์ที่พิมพ์บนหน้าจอ
ตัวอย่างอื่น:
#include <iostream> #include <algorithm> #include <vector> int main() { std::vector<int> vec = { 1, 2, 3, 4, 5 }; int sum = 0; std::for_each(vec.begin(), vec.end(), [&sum](int x) { sum += x; }); std::cout << "The sum is: " << sum << std::endl; return 0; }
ในกรณีนี้ นิพจน์แลมบ์ดา [&sum](int x) { sum += x; }
จะถูกส่งผ่านเป็นฟังก์ชันที่จะใช้กับแต่ละองค์ประกอบ แลมบ์ดาจับตัวแปร sum
โดยการอ้างอิง &
เพื่อให้สามารถแก้ไขได้ภายในเนื้อความแลมบ์ดา
ทั้งสองตัวอย่างได้ผลลัพธ์เดียวกัน แต่ตัวอย่างที่สองใช้อัลกอริธึม std::for_each
และนิพจน์แลมบ์ดา ซึ่งเป็นเทคนิคที่ทันสมัยและกระชับมากกว่าใน C++
{2} การบันทึกค่าเริ่มต้น
เมื่อมีการประกาศนิพจน์แลมบ์ดาโดยไม่มีการจับที่ชัดเจน การทำงานเริ่มต้นคือการจับตัวแปรในขอบเขตโดยรอบโดยการอ้างอิง สิ่งนี้เรียกว่าการจับภาพเริ่มต้น
ตัวอย่าง:
#include <iostream> int main() { int x = 42; auto f = [ ]() { std::cout << x << std::endl; }; f(); return 0; } #include <iostream> int main() { auto square = [](int x) { return x * x; }; std::cout << "The square of 5 is: " << square(5) << std::endl; return 0; }
ในตัวอย่างที่สอง นิพจน์แลมบ์ดาถูกกำหนดและจัดเก็บไว้ในตัวแปรชื่อ square
นิพจน์แลมบ์ดานี้รับอาร์กิวเมนต์ int
x
และส่งกลับค่า x * x
ซึ่งเป็นกำลังสองของอาร์กิวเมนต์
ในฟังก์ชัน main
นิพจน์แลมบ์ดานี้จะใช้เป็นออบเจ็กต์ฟังก์ชัน มันถูกเรียกโดยการส่งอาร์กิวเมนต์ 5
และผลลัพธ์จะแสดงโดยใช้สตรีม cout
{3} การจับตามค่า
นี่เป็นรูปแบบที่ง่ายที่สุดของนิพจน์ lambda โดยที่คุณส่งตัวแปรไปยังฟังก์ชันตามค่า เมื่อตัวแปรถูกจับตามค่า ค่าปัจจุบันจะถูกเก็บไว้ในการปิดและจะไม่อัปเดตเมื่อตัวแปรเปลี่ยนแปลงในขอบเขตโดยรอบ ซึ่งทำได้โดยการรวมตัวแปรไว้ในวงเล็บเหลี่ยม [ ]
ตัวอย่าง
#include <iostream> int main() { int x = 42; auto f = [x]() { std::cout << x << std::endl; }; f(); return 0; } #include <iostream> int main() { int x = 42; auto f = [x](int y) { std::cout << x+y << std::endl;}; f(1); return 0; }
{4} การจับโดยการอ้างอิง
คุณสามารถส่งผ่านตัวแปรไปยังนิพจน์แลมบ์ดาโดยการอ้างอิงโดยใช้สัญลักษณ์ &
เมื่อตัวแปรถูกจับโดยการอ้างอิง ค่าปัจจุบันจะถูกเก็บไว้ในการปิดและได้รับการอัปเดตเมื่อตัวแปรเปลี่ยนแปลงในขอบเขตโดยรอบ ซึ่งทำได้โดยการรวมที่อยู่ของตัวดำเนินการ &
ไว้หน้าตัวแปรในวงเล็บเหลี่ยม [ ]
ตัวอย่าง
#include <iostream> int main() { int x = 42; auto f = [&x]() { std::cout << x << std::endl; }; f(); return 0; } #include <iostream> int main() { int x = 10; auto add_one = [&x]() { ++x; }; add_one(); std::cout << x << "\n"; return 0; } #include <iostream> int main() { int x = 42; auto f = [&x]() { std::cout << x << std::endl; }; f(); return 0; }
ในตัวอย่างสุดท้าย ตัวแปร x
ถูกจับโดยการอ้างอิง และ lambda add
สามารถแก้ไขค่าของมันได้
{5} แลมบ์ดาที่ไม่แน่นอน
ตามค่าเริ่มต้น ตัวแปรที่นิพจน์ lambda จับไว้จะเป็นค่าคงที่และไม่สามารถแก้ไขได้ภายในเนื้อความของนิพจน์ lambda หากคุณต้องการแก้ไขตัวแปรที่บันทึกไว้ในนิพจน์ lambda คุณสามารถทำให้นิพจน์ lambda ไม่แน่นอนได้ แลมบ์ดาที่ไม่แน่นอนช่วยให้สามารถแก้ไขตัวแปรที่บันทึกไว้ได้ ซึ่งทำได้โดยการรวมคีย์เวิร์ด mutable
ไว้ในวงเล็บเหลี่ยม [ ]
ตัวอย่าง
#include <iostream> int main() { int x = 42; auto f = [x]() mutable { std::cout << ++x << std::endl; }; f(); return 0; }
{บทสรุป}
นิพจน์แลมบ์ดาคล้ายกับฟังก์ชันปกติ แต่มีความแตกต่างที่สำคัญบางประการ ตัวอย่างเช่น ประเภทของนิพจน์แลมบ์ดาไม่ได้ระบุอย่างชัดเจน แต่คอมไพลเลอร์สามารถอนุมานได้ นอกจากนี้ นิพจน์ lambda ยังสามารถจับตัวแปรจากขอบเขตโดยรอบได้ ทำให้มีประโยชน์มากสำหรับการสร้างการปิดและการทำงานกับแนวคิดการเขียนโปรแกรมเชิงฟังก์ชันใน C++
Lambdas มีประโยชน์ด้านประสิทธิภาพมากกว่าฟังก์ชันแบบเดิม
- ฟังก์ชันอินไลน์: Lambdas จะถูกอินไลน์โดยอัตโนมัติโดยคอมไพเลอร์ ซึ่งหมายความว่าโค้ดของแลมบ์ดาจะถูกแทรกลงในฟังก์ชันการเรียกโดยตรง ซึ่งสามารถลดค่าใช้จ่ายในการเรียกใช้ฟังก์ชันและปรับปรุงประสิทธิภาพได้
- การหลีกเลี่ยงโอเวอร์เฮดของฟังก์ชันที่มีชื่อ: Lambdas ไม่มีชื่อ ดังนั้นจึงไม่จำเป็นต้องประกาศและจัดเก็บไว้ในตารางสัญลักษณ์ ซึ่งสามารถลดโอเวอร์เฮดและปรับปรุงประสิทธิภาพได้
- ปรับปรุงตำแหน่งแคช: สามารถกำหนดและใช้งาน Lambdas ภายในฟังก์ชันเดียวกันได้ ซึ่งหมายความว่ารหัสและข้อมูลที่ใช้โดย lambda จะถูกจัดเก็บไว้ในบรรทัดแคชเดียวกันกับรหัสการเรียก วิธีนี้สามารถปรับปรุงตำแหน่งแคชและลดต้นทุนการพลาดแคชได้
- ขนาดโค้ดที่ลดลง: โดยปกติแล้ว Lambdas จะมีขนาดเล็กกว่าฟังก์ชันที่มีชื่อ และไม่จำเป็นต้องมีการเรียกใช้ฟังก์ชันภายนอก ซึ่งสามารถลดขนาดของโค้ดที่คอมไพล์และปรับปรุงประสิทธิภาพได้
- ความยืดหยุ่นที่เพิ่มขึ้น: Lambdas สามารถใช้เพื่อส่งฟังก์ชันเป็นอาร์กิวเมนต์ไปยังฟังก์ชันอื่นๆ ได้ ซึ่งให้ความยืดหยุ่นมากขึ้นในการนำโค้ดกลับมาใช้ซ้ำและจัดระเบียบได้ วิธีนี้สามารถปรับปรุงประสิทธิภาพได้โดยลดความจำเป็นในการใช้โค้ดที่ซ้ำกัน
- ปรับปรุงความสามารถในการอ่าน: Lambdas สามารถทำให้โค้ดอ่านง่ายขึ้นโดยการห่อหุ้มตรรกะที่ซับซ้อนในลักษณะที่กะทัดรัดและครบถ้วนในตัวเอง สิ่งนี้สามารถปรับปรุงประสิทธิภาพโดยทำให้ง่ายต่อการเข้าใจและบำรุงรักษาโค้ด
โดยสรุป lambdas สามารถมอบประสิทธิภาพที่ได้รับการปรับปรุงให้ดีขึ้นกว่าฟังก์ชันแบบเดิมโดยลดค่าใช้จ่าย ปรับปรุงตำแหน่งแคช ลดขนาดโค้ด เพิ่มความยืดหยุ่น และปรับปรุงความสามารถในการอ่าน
ดีที่สุด