Ch14: Lambda functions¶
TL;DR: Anonymous Functionality: Lambda Function คือฟังก์ชันที่ไม่มีชื่อ (Anonymous Function) ซึ่งช่วยอำนวยความสะดวกในการนิยามฟังก์ชันแบบ inline ในตําแหน่งที่ต้องการใช้งานได้ทันที
⚡ Quick Reference¶
#include <iostream>
int main() {
// [capture](params) -> ret { body }; - ไวยากรณ์เต็มรูปแบบของ Lambda
auto full_lambda = [](double a, double b) -> double { return a + b; };
// auto func = []{ ... }; - กำหนด Lambda ให้กับตัวแปรประเภท auto
auto simple_lambda = []() { std::cout << "Simple" << std::endl; };
simple_lambda();
// []{ ... }(); - ประกาศสร้างและเรียกใช้งาน Lambda ทันที
[]() { std::cout << "One-shot" << std::endl; }();
int a = 10, b = 20;
// [a, b] - ดึงตัวแปรที่ระบุเข้ามาแบบสำเนาค่า (By value)
auto val_lambda = [a, b]() { return a + b; };
// [&a, &b] - ดึงตัวแปรที่ระบุเข้ามาแบบอ้างอิงตำแหน่งในแรม (By reference)
auto ref_lambda = [&a, &b]() { a += 10; };
// [=] - ดึงตัวแปรภายนอกทั้งหมดเข้ามาแบบสำเนาค่า (By value)
auto capture_all_val = [=]() { return a + b; };
// [&] - ดึงตัวแปรภายนอกทั้งหมดเข้ามาแบบอ้างอิงตำแหน่งในแรม (By reference)
auto capture_all_ref = [&]() { a = 100; };
// [] -> int { ... } - ระบุประเภทข้อมูลส่งกลับของ Lambda ชัดเจน
auto forced_ret = []() -> int { return 42; };
// เรียกใช้ Lambda พร้อมอาร์กิวเมนต์เพื่อทำงานทันที
double sum = [](double x, double y){ return x + y; }(10.5, 20.5);
return 0;
}
🧠 Core Concepts¶
- Anonymous Functionality: Lambda Function คือฟังก์ชันที่ไม่มีชื่อ (Anonymous Function) ซึ่งช่วยอำนวยความสะดวกในการนิยามฟังก์ชันแบบ inline ในตําแหน่งที่ต้องการใช้งานได้ทันที
- Variable Capture: Lambda จะเข้าถึงตัวแปรภายนอกได้ก็ต่อเมื่อระบุใน Capture List (
[]) เท่านั้น โดยโดยเริ่มต้นจะไม่สามารถอ่านเขียนตัวแปรภายนอกสโคปได้เลย - Return Type Deduction: Compiler จะช่วยคาดเดาประเภทข้อมูลส่งกลับของ Lambda ให้โดยอัตโนมัติ แต่ผู้พัฒนาสามารถระบุประเภทเองได้ชัดเจนผ่านไวยากรณ์ลูกศรชี้ออก
->
⚠️ Pitfalls (Quick Scan)¶
| ข้อผิดพลาด | วิธีแก้ |
|---|---|
| ประกาศสร้าง Lambda โดยไม่ได้เรียกคำสั่งให้ทำงานหรือเก็บค่าลงตัวแปร | เรียกทำงานทันทีโดยใส่วงเล็บ () ต่อท้าย หรือเก็บค่าไว้ในตัวแปร auto |
| เรียกใช้งานตัวแปรภายนอกในบล็อก Lambda โดยไม่เขียนไว้ใน capture list | เขียนระบุตัวแปรที่ต้องการนำเข้ามาใช้งานในวงเล็บเหลี่ยม [] |
| คาดหวังให้ตัวแปรภายนอกที่เปลี่ยนไปส่งผลต่อข้อมูลใน Lambda ที่ดึงค่าแบบส่งผ่านค่า (Capture by value) | ดึงตัวแปรแบบอ้างอิง ([&]) หากต้องการให้สะท้อนค่าที่เปลี่ยนแปลงล่าสุด |
| ดึงตัวแปรภายนอกแบบอ้างอิงทิ้งไว้ใน Lambda แต่ตัวแปรต้นฉบับหมดอายุขัยไปก่อนเรียกใช้ | ตรวจสอบขอบเขตอายุขัย (Lifetime) ของตัวแปรภายนอกให้ครอบคลุมการทำงานของ Lambda |
| ระบุประเภทการส่งคืนแบบแคบลง (เช่น ` | ตัวเลขจะถูกปัดเศษหรือตัดส่วนท้ายออกเงียบๆ ทำให้สูญเสียความแม่นยำ |
| ส่งผ่านค่าคนละชนิดเข้าไปในพารามิเตอร์ของ Lambda | ป้อนอาร์กิวเมนต์ให้ตรงประเภทกับที่ระบุในรายการพารามิเตอร์ |
📖 Full Details¶
Cause → Effect → Fix พร้อม timestamp (คลิกเพื่อดู)
* **ประกาศสร้าง Lambda โดยไม่ได้เรียกคำสั่งให้ทำงานหรือเก็บค่าลงตัวแปร** -> **โค้ดผ่านการ Compile ตามปกติแต่ไม่มีอะไรทำงานเลย** -> **เรียกทำงานทันทีโดยใส่วงเล็บ `()` ต่อท้าย หรือเก็บค่าไว้ในตัวแปร `auto` (00:04:17)** * **เรียกใช้งานตัวแปรภายนอกในบล็อก Lambda โดยไม่เขียนไว้ใน capture list** -> **เกิด Compile-time error แจ้งเตือนว่าห้ามอ้างอิงตัวแปรภายนอก** -> **เขียนระบุตัวแปรที่ต้องการนำเข้ามาใช้งานในวงเล็บเหลี่ยม `[]` (00:15:51)** * **คาดหวังให้ตัวแปรภายนอกที่เปลี่ยนไปส่งผลต่อข้อมูลใน Lambda ที่ดึงค่าแบบส่งผ่านค่า (Capture by value)** -> **ตัวแปรภายใน Lambda จะยึดตามค่าที่คัดลอกไว้ตั้งแต่จังหวะสร้างฟังก์ชันเท่านั้น** -> **ดึงตัวแปรแบบอ้างอิง (`[&]`) หากต้องการให้สะท้อนค่าที่เปลี่ยนแปลงล่าสุด (00:14:30)** * **ดึงตัวแปรภายนอกแบบอ้างอิงทิ้งไว้ใน Lambda แต่ตัวแปรต้นฉบับหมดอายุขัยไปก่อนเรียกใช้** -> **เกิด Undefined behavior หรือโปรแกรมแครชเนื่องจากอ่านค่าตำแหน่งขยะ** -> **ตรวจสอบขอบเขตอายุขัย (Lifetime) ของตัวแปรภายนอกให้ครอบคลุมการทำงานของ Lambda (00:18:30)** * **ระบุประเภทการส่งคืนแบบแคบลง (เช่น `-> int`) เกินผลลัพธ์การคำนวณจริง** -> **ตัวเลขจะถูกปัดเศษหรือตัดส่วนท้ายออกเงียบๆ ทำให้สูญเสียความแม่นยำ** -> **เลือกประเภทผลลัพธ์ให้ครอบคลุมขนาดข้อมูลเพื่อเลี่ยง narrowing (00:12:27)** * **ส่งผ่านค่าคนละชนิดเข้าไปในพารามิเตอร์ของ Lambda** -> **Compiler จะลอบทำการแปลงประเภทข้อมูลโดยนัย ซึ่งเสี่ยงทำให้ข้อมูลคลาดเคลื่อน** -> **ป้อนอาร์กิวเมนต์ให้ตรงประเภทกับที่ระบุในรายการพารามิเตอร์ (00:07:56)**📎 Repo Files¶
21.LambdaFunctions/21.2DeclaringAndUsingLambdas/main.cpp21.LambdaFunctions/21.3CaptureLists/main.cpp21.LambdaFunctions/21.4CaptureAllLists/main.cpp22.FunctionsTheMisfits/22.2StaticVariables/main.cpp22.FunctionsTheMisfits/22.3InlineFunctions/main.cpp22.FunctionsTheMisfits/22.4RecursiveFunctions/main.cpp23.FunctionCallStackD_ebugging/23.4D_ebuggingInV_S_C0de/main.cpp23.FunctionCallStackD_ebugging/23.7D_ebuggingArraysLoopsAndPointers/main.cpp