ทำไมต้องใช้ Lambda Expression?
พิจารณาข้อความต่อไปนี้:
intmyInt= 52;ที่นี่ myInt เป็นตัวระบุ lvalue 52 เป็นตัวอักษร prvalue วันนี้ เป็นไปได้ที่จะเขียนโค้ดฟังก์ชันพิเศษและวางไว้ในตำแหน่ง 52 ฟังก์ชันดังกล่าวเรียกว่านิพจน์แลมบ์ดา พิจารณาโปรแกรมสั้น ๆ ต่อไปนี้ด้วย:
#รวม
โดยใช้ เนมสเปซชั่วโมง;
intfn(intผ่าน)
{
intคำตอบ=ผ่าน+ 3;
กลับคำตอบ;
}
intหลัก()
{
fn(5);
กลับ 0;
}
วันนี้ เป็นไปได้ที่จะเขียนโค้ดฟังก์ชันพิเศษและวางไว้ในตำแหน่งอาร์กิวเมนต์ที่ 5 ของการเรียกใช้ฟังก์ชัน fn(5) ฟังก์ชันดังกล่าวเรียกว่านิพจน์แลมบ์ดา นิพจน์แลมบ์ดา (ฟังก์ชัน) ในตำแหน่งนั้นเป็นค่า prvalue
ลิเทอรัลใดๆ ยกเว้นสตริงลิเทอรัลคือ prvalue นิพจน์แลมบ์ดาเป็นการออกแบบฟังก์ชันพิเศษที่พอดีกับตัวอักษรในโค้ด เป็นฟังก์ชันที่ไม่ระบุชื่อ (ไม่มีชื่อ) บทความนี้อธิบายนิพจน์หลัก C++ ใหม่ ซึ่งเรียกว่านิพจน์แลมบ์ดา ความรู้พื้นฐานใน C ++ เป็นข้อกำหนดในการทำความเข้าใจบทความนี้
เนื้อหาบทความ
- ภาพประกอบของ Lambda Expression
- ส่วนของนิพจน์แลมบ์ดา
- จับภาพ
- แผนฟังก์ชันการโทรกลับแบบคลาสสิกพร้อม Lambda Expression
- แบบต่อท้าย-return-type
- ปิด
- บทสรุป
ภาพประกอบของ Lambda Expression
ในโปรแกรมต่อไปนี้ ฟังก์ชันซึ่งเป็นนิพจน์แลมบ์ดาถูกกำหนดให้กับตัวแปร:
#รวม
โดยใช้ เนมสเปซชั่วโมง;
รถยนต์fn= [](intหยุด)
{
intคำตอบ=หยุด+ 3;
กลับคำตอบ;
};
intหลัก()
{
รถยนต์ตัวแปร=fn(2);
ค่าใช้จ่าย <<ตัวแปร<< 'NS';
กลับ 0;
}
ผลลัพธ์คือ:
5นอกฟังก์ชัน main() จะมีตัวแปร fn ประเภทของมันคือรถยนต์ อัตโนมัติในสถานการณ์นี้หมายความว่าประเภทจริง เช่น int หรือ float ถูกกำหนดโดยตัวถูกดำเนินการทางขวาของตัวดำเนินการกำหนด (=) ทางด้านขวาของตัวดำเนินการกำหนดคือนิพจน์แลมบ์ดา นิพจน์แลมบ์ดาเป็นฟังก์ชันที่ไม่มีประเภทส่งคืนก่อนหน้า สังเกตการใช้และตำแหน่งของวงเล็บเหลี่ยม [] ฟังก์ชันส่งคืน 5 ซึ่งเป็น int ซึ่งจะกำหนดประเภทของ fn
ในฟังก์ชัน main() มีคำสั่งดังนี้
รถยนต์ตัวแปร=fn(2);ซึ่งหมายความว่า fn นอก main() ลงเอยด้วยตัวระบุสำหรับฟังก์ชัน พารามิเตอร์โดยนัยคือพารามิเตอร์ของนิพจน์แลมบ์ดา ประเภทของตัวแปรเป็นแบบอัตโนมัติ
โปรดทราบว่านิพจน์แลมบ์ดาลงท้ายด้วยเครื่องหมายอัฒภาค เช่นเดียวกับคำจำกัดความของคลาสหรือโครงสร้าง ที่ลงท้ายด้วยเครื่องหมายอัฒภาค
ในโปรแกรมต่อไปนี้ ฟังก์ชัน ซึ่งเป็นนิพจน์แลมบ์ดาที่คืนค่า 5 เป็นอาร์กิวเมนต์ของฟังก์ชันอื่น:
#รวมโดยใช้ เนมสเปซชั่วโมง;
โมฆะอื่นๆ(intที่ 1int (*ptr)(int))
{
intno2= (*ptr)(2);
ค่าใช้จ่าย <<no1<< '' <<no2<< 'NS';
}
intหลัก()
{
อื่นๆ(4,[](intหยุด)
{
intคำตอบ=หยุด+ 3;
กลับคำตอบ;
});
กลับ 0;
}
ผลลัพธ์คือ:
สี่ห้ามีสองฟังก์ชันที่นี่ นิพจน์แลมบ์ดาและฟังก์ชัน otherfn() นิพจน์แลมบ์ดาเป็นอาร์กิวเมนต์ที่สองของ otherfn() ที่เรียกว่า main() โปรดทราบว่าฟังก์ชันแลมบ์ดา (นิพจน์) ไม่ได้ลงท้ายด้วยเครื่องหมายอัฒภาคในการเรียกนี้ เนื่องจากเป็นอาร์กิวเมนต์ (ไม่ใช่ฟังก์ชันสแตนด์อะโลน)
พารามิเตอร์ฟังก์ชันแลมบ์ดาในนิยามของฟังก์ชัน otherfn() เป็นตัวชี้ไปยังฟังก์ชัน ตัวชี้มีชื่อ ptr. ชื่อ ptr ใช้ในนิยาม otherfn() เพื่อเรียกใช้ฟังก์ชันแลมบ์ดา
แถลงการณ์
intno2= (*ptr)(2);ในนิยาม otherfn() จะเรียกใช้ฟังก์ชันแลมบ์ดาด้วยอาร์กิวเมนต์ 2 ค่าส่งคืนของการเรียก '(*ptr)(2)' จากฟังก์ชันแลมบ์ดา ถูกกำหนดให้กับ no2
โปรแกรมข้างต้นยังแสดงให้เห็นว่าฟังก์ชันแลมบ์ดาสามารถใช้ในรูปแบบฟังก์ชันการโทรกลับ C++ ได้อย่างไร
ส่วนของนิพจน์แลมบ์ดา
ส่วนของฟังก์ชันแลมบ์ดาทั่วไปมีดังนี้:
[] () {}- [] เป็นประโยคจับ มันสามารถมีรายการ
- () ใช้สำหรับรายการพารามิเตอร์
- {} ใช้สำหรับเนื้อหาฟังก์ชัน หากฟังก์ชันทำงานโดยลำพัง ก็ควรลงท้ายด้วยเครื่องหมายอัฒภาค
จับภาพ
นิยามฟังก์ชันแลมบ์ดาสามารถกำหนดให้กับตัวแปรหรือใช้เป็นอาร์กิวเมนต์สำหรับการเรียกใช้ฟังก์ชันอื่นได้ คำจำกัดความสำหรับการเรียกใช้ฟังก์ชันดังกล่าวควรมีเป็นพารามิเตอร์ ตัวชี้ไปยังฟังก์ชัน ซึ่งสอดคล้องกับนิยามฟังก์ชันแลมบ์ดา
นิยามฟังก์ชันแลมบ์ดาแตกต่างจากนิยามฟังก์ชันปกติ สามารถกำหนดให้กับตัวแปรในขอบเขตส่วนกลาง ฟังก์ชันที่กำหนดให้กับตัวแปรนี้ยังสามารถเข้ารหัสภายในฟังก์ชันอื่นได้อีกด้วย เมื่อกำหนดให้กับตัวแปรขอบเขตส่วนกลาง เนื้อหาของตัวแปรนั้นสามารถเห็นตัวแปรอื่นๆ ในขอบเขตส่วนกลางได้ เมื่อกำหนดให้กับตัวแปรภายในนิยามฟังก์ชันปกติ เนื้อหาของตัวแปรนั้นสามารถเห็นตัวแปรอื่นๆ ในขอบเขตของฟังก์ชันด้วยความช่วยเหลือของคำสั่งการดักจับ [] เท่านั้น
ประโยคการดักจับ [] หรือที่เรียกว่า lambda-introducer อนุญาตให้ส่งตัวแปรจากขอบเขต (ฟังก์ชัน) โดยรอบไปยังเนื้อหาฟังก์ชันของนิพจน์ lambda เนื้อหาฟังก์ชันของนิพจน์แลมบ์ดากล่าวเพื่อจับตัวแปรเมื่อได้รับวัตถุ หากไม่มีส่วนคำสั่งการดักจับ [] ตัวแปรจะไม่สามารถส่งจากขอบเขตโดยรอบไปยังเนื้อหาฟังก์ชันของนิพจน์แลมบ์ดาได้ โปรแกรมต่อไปนี้แสดงสิ่งนี้ โดยมีขอบเขตฟังก์ชัน main() เป็นขอบเขตโดยรอบ:
#รวมโดยใช้ เนมสเปซชั่วโมง;
intหลัก()
{
intNS= 5;
รถยนต์fn= [NS]()
{
ค่าใช้จ่าย <<NS<< 'NS';
};
fn();
กลับ 0;
}
ผลลัพธ์คือ 5 . หากไม่มีชื่อ id ภายใน [] นิพจน์แลมบ์ดาจะไม่เห็นตัวแปร id ของขอบเขตฟังก์ชัน main()
จับภาพโดยการอ้างอิง
ตัวอย่างการใช้ส่วนคำสั่งการจับภาพด้านบนเป็นการจับภาพตามค่า (ดูรายละเอียดด้านล่าง) ในการจับภาพโดยการอ้างอิง ตำแหน่ง (ที่เก็บข้อมูล) ของตัวแปร เช่น id ด้านบน ของขอบเขตโดยรอบ จะพร้อมใช้งานภายในเนื้อหาฟังก์ชันแลมบ์ดา ดังนั้น การเปลี่ยนค่าของตัวแปรภายในเนื้อหาฟังก์ชันแลมบ์ดาจะเปลี่ยนค่าของตัวแปรเดียวกันนั้นในขอบเขตโดยรอบ ตัวแปรแต่ละตัวที่ทำซ้ำในคำสั่งการดักจับจะถูกนำหน้าด้วยเครื่องหมายและ (&) เพื่อให้บรรลุเป้าหมายนี้ โปรแกรมต่อไปนี้แสดงให้เห็นสิ่งนี้:
#รวมโดยใช้ เนมสเปซชั่วโมง;
intหลัก()
{
intNS= 5; ลอยฟุต= 2.3; charch= 'ถึง';
รถยนต์fn= [&NS,&ฟุต,&ch]()
{
NS= 6;ฟุต= 3.4;ch= 'NS';
};
fn();
ค่าใช้จ่าย <<NS<< ',' <<ฟุต<< ',' <<ch<< 'NS';
กลับ 0;
}
ผลลัพธ์คือ:
6, 3.4, Bการยืนยันว่าชื่อตัวแปรภายในเนื้อหาฟังก์ชันของนิพจน์แลมบ์ดามีไว้สำหรับตัวแปรเดียวกันนอกนิพจน์แลมบ์ดา
จับตามค่า
ในการจับภาพตามค่า สำเนาตำแหน่งของตัวแปร ขอบเขตโดยรอบ จะพร้อมใช้งานภายในเนื้อหาฟังก์ชันแลมบ์ดา แม้ว่าตัวแปรภายในเนื้อความของฟังก์ชันแลมบ์ดาจะเป็นสำเนา แต่ค่าของตัวแปรภายในเนื้อความไม่สามารถเปลี่ยนแปลงได้ในขณะนี้ เพื่อให้บรรลุการดักจับตามค่า ตัวแปรแต่ละตัวที่ทำซ้ำในคำสั่งย่อยการดักจับจะไม่ถูกนำหน้าด้วยสิ่งใด โปรแกรมต่อไปนี้แสดงให้เห็นสิ่งนี้:
#รวมโดยใช้ เนมสเปซชั่วโมง;
intหลัก()
{
intNS= 5; ลอยฟุต= 2.3; charch= 'ถึง';
รถยนต์fn= [id ฟุต ch]()
{
//id = 6; ฟุต = 3.4; ch = 'B';
ค่าใช้จ่าย <<NS<< ',' <<ฟุต<< ',' <<ch<< 'NS';
};
fn();
NS= 6;ฟุต= 3.4;ch= 'NS';
ค่าใช้จ่าย <<NS<< ',' <<ฟุต<< ',' <<ch<< 'NS';
กลับ 0;
}
ผลลัพธ์คือ:
5, 2.3, A6, 3.4, B
หากลบตัวบ่งชี้ความคิดเห็น โปรแกรมจะไม่คอมไพล์ คอมไพเลอร์จะออกข้อความแสดงข้อผิดพลาดว่าตัวแปรภายในคำจำกัดความของนิพจน์แลมบ์ดาของตัวฟังก์ชันไม่สามารถเปลี่ยนแปลงได้ แม้ว่าตัวแปรภายในฟังก์ชันแลมบ์ดาจะไม่สามารถเปลี่ยนแปลงได้ แต่ก็สามารถเปลี่ยนแปลงได้นอกฟังก์ชันแลมบ์ดาตามที่เอาต์พุตของโปรแกรมด้านบนแสดง
ผสมแคปเจอร์
การจับภาพโดยการอ้างอิงและการบันทึกโดยค่าสามารถผสมกันได้ ดังที่แสดงในโปรแกรมต่อไปนี้:
#รวมโดยใช้ เนมสเปซชั่วโมง;
intหลัก()
{
intNS= 5; ลอยฟุต= 2.3; charch= 'ถึง'; boolบลู= จริง;
รถยนต์fn= [รหัส, ฟุต,&ช,&บลู]()
{
ch= 'NS';บลู= เท็จ;
ค่าใช้จ่าย <<NS<< ',' <<ฟุต<< ',' <<ch<< ',' <<บลู<< 'NS';
};
fn();
กลับ 0;
}
ผลลัพธ์คือ:
5, 2.3, B, 0เมื่อถูกจับทั้งหมดมีการอ้างอิง:
หากตัวแปรทั้งหมดที่จะถูกจับถูกจับโดยการอ้างอิง เพียงหนึ่ง & ก็เพียงพอแล้วในประโยคการดักจับ โปรแกรมต่อไปนี้แสดงให้เห็นสิ่งนี้:
#รวมโดยใช้ เนมสเปซชั่วโมง;
intหลัก()
{
intNS= 5; ลอยฟุต= 2.3; charch= 'ถึง'; boolบลู= จริง;
รถยนต์fn= [&]()
{
NS= 6;ฟุต= 3.4;ch= 'NS';บลู= เท็จ;
};
fn();
ค่าใช้จ่าย <<NS<< ',' <<ฟุต<< ',' <<ch<< ',' <<บลู<< 'NS';
กลับ 0;
}
ผลลัพธ์คือ:
6, 3.4, B, 0หากตัวแปรบางตัวถูกจับโดยการอ้างอิงและอื่น ๆ ด้วยค่า หนึ่ง & จะแสดงการอ้างอิงทั้งหมด และส่วนที่เหลือจะไม่นำหน้าด้วยสิ่งใด ดังที่แสดงในโปรแกรมต่อไปนี้:
โดยใช้ เนมสเปซชั่วโมง;intหลัก()
{
intNS= 5; ลอยฟุต= 2.3; charch= 'ถึง'; boolบลู= จริง;
รถยนต์fn= [&, id, ft]()
{
ch= 'NS';บลู= เท็จ;
ค่าใช้จ่าย <<NS<< ',' <<ฟุต<< ',' <<ch<< ',' <<บลู<< 'NS';
};
fn();
กลับ 0;
}
ผลลัพธ์คือ:
5, 2.3, B, 0โปรดทราบว่า & เพียงอย่างเดียว (เช่น & ไม่ตามด้วยตัวระบุ) ต้องเป็นอักขระตัวแรกในประโยคการจับภาพ
เมื่อจับได้หมดก็นับตามมูลค่า:
หากตัวแปรทั้งหมดที่จะถูกจับต้องถูกจับด้วยค่า ดังนั้นเพียง = หนึ่งตัวก็เพียงพอแล้วในคำสั่งย่อยการดักจับ โปรแกรมต่อไปนี้แสดงให้เห็นสิ่งนี้:
#รวมโดยใช้ เนมสเปซชั่วโมง;
intหลัก()
{
intNS= 5; ลอยฟุต= 2.3; charch= 'ถึง'; boolบลู= จริง;
รถยนต์fn= [=]()
{
ค่าใช้จ่าย <<NS<< ',' <<ฟุต<< ',' <<ch<< ',' <<บลู<< 'NS';
};
fn();
กลับ 0;
}
ผลลัพธ์คือ:
5, 2.3, A, 1บันทึก : = เป็นแบบอ่านอย่างเดียว ณ ตอนนี้
หากตัวแปรบางตัวถูกจับโดยค่าและอื่น ๆ โดยการอ้างอิง หนึ่ง = จะแสดงตัวแปรที่คัดลอกแบบอ่านอย่างเดียวทั้งหมด และส่วนที่เหลือแต่ละรายการจะมี & ตามที่โปรแกรมต่อไปนี้แสดง:
#รวมโดยใช้ เนมสเปซชั่วโมง;
intหลัก()
{
intNS= 5; ลอยฟุต= 2.3; charch= 'ถึง'; boolบลู= จริง;
รถยนต์fn= [=,&ช,&บลู]()
{
ch= 'NS';บลู= เท็จ;
ค่าใช้จ่าย <<NS<< ',' <<ฟุต<< ',' <<ch<< ',' <<บลู<< 'NS';
};
fn();
กลับ 0;
}
ผลลัพธ์คือ:
5, 2.3, B, 0โปรดทราบว่า = alone ต้องเป็นอักขระตัวแรกในประโยคการดักจับ
แผนฟังก์ชันการโทรกลับแบบคลาสสิกพร้อม Lambda Expression
โปรแกรมต่อไปนี้แสดงให้เห็นว่าโครงร่างฟังก์ชันการเรียกกลับแบบคลาสสิกสามารถทำได้ด้วยนิพจน์แลมบ์ดา:
#รวมโดยใช้ เนมสเปซชั่วโมง;
char *ผลผลิต;
รถยนต์cba= [](charออก[])
{
ผลผลิต=ออก;
};
โมฆะหลักFunc(charป้อนข้อมูล[],โมฆะ (*สำหรับ)(char[]))
{
(*สำหรับ)(ป้อนข้อมูล);
ค่าใช้จ่าย<<'สำหรับหน้าที่หลัก'<<'NS';
}
โมฆะfn()
{
ค่าใช้จ่าย<<'ตอนนี้'<<'NS';
}
intหลัก()
{
charป้อนข้อมูล[] = 'สำหรับฟังก์ชั่นการโทรกลับ';
หลักFunc(อินพุต cba);
fn();
ค่าใช้จ่าย<<ผลผลิต<<'NS';
กลับ 0;
}
ผลลัพธ์คือ:
เพื่อทำหน้าที่หลักตอนนี้
สำหรับฟังก์ชั่นการโทรกลับ
โปรดจำไว้ว่าเมื่อมีการกำหนดนิยามนิพจน์แลมบ์ดาให้กับตัวแปรในขอบเขตส่วนกลาง เนื้อหาของฟังก์ชันนั้นสามารถเห็นตัวแปรส่วนกลางโดยไม่ต้องใช้ส่วนคำสั่งการดักจับ
แบบต่อท้าย-return-type
ประเภทการส่งคืนของนิพจน์แลมบ์ดาเป็นแบบอัตโนมัติ หมายความว่าคอมไพเลอร์กำหนดประเภทการส่งคืนจากนิพจน์การส่งคืน (ถ้ามี) หากโปรแกรมเมอร์ต้องการระบุประเภทการส่งคืนจริง ๆ เขาจะทำตามในโปรแกรมต่อไปนี้:
#รวมโดยใช้ เนมสเปซชั่วโมง;
รถยนต์fn= [](intหยุด) -> int
{
intคำตอบ=หยุด+ 3;
กลับคำตอบ;
};
intหลัก()
{
รถยนต์ตัวแปร=fn(2);
ค่าใช้จ่าย <<ตัวแปร<< 'NS';
กลับ 0;
}
ผลลัพธ์คือ 5 หลังจากรายการพารามิเตอร์ ตัวดำเนินการลูกศรจะถูกพิมพ์ ตามด้วยประเภทการส่งคืน (int ในกรณีนี้)
ปิด
พิจารณาส่วนรหัสต่อไปนี้:
โครงสร้างคลา{
intNS= 5;
charch= 'ถึง';
}obj1, obj2;
ที่นี่ Cla เป็นชื่อของคลาส struct Obj1 และ obj2 เป็นสองอ็อบเจ็กต์ที่จะสร้างอินสแตนซ์จากคลาส struct นิพจน์แลมบ์ดามีความคล้ายคลึงในการใช้งาน นิยามฟังก์ชันแลมบ์ดาเป็นคลาสชนิดหนึ่ง เมื่อเรียกใช้ฟังก์ชันแลมบ์ดา (เรียก) อ็อบเจ็กต์จะถูกสร้างอินสแตนซ์จากคำจำกัดความ วัตถุนี้เรียกว่าการปิด มันคือการปิดที่ทำงานที่แลมบ์ดาคาดว่าจะทำ
อย่างไรก็ตาม การเข้ารหัสนิพจน์แลมบ์ดาเหมือนโครงสร้างด้านบนจะมี obj1 และ obj2 แทนที่ด้วยอาร์กิวเมนต์ของพารามิเตอร์ที่เกี่ยวข้อง โปรแกรมต่อไปนี้แสดงให้เห็นสิ่งนี้:
#รวมโดยใช้ เนมสเปซชั่วโมง;
รถยนต์fn= [](intพารามิเตอร์1,intparam2)
{
intคำตอบ=พาราม1+param2;
กลับคำตอบ;
} (2,3);
intหลัก()
{
รถยนต์ที่ไหน=fn;
ค่าใช้จ่าย <<ที่ไหน<< 'NS';
กลับ 0;
}
ผลลัพธ์คือ 5 อาร์กิวเมนต์คือ 2 และ 3 ในวงเล็บ โปรดทราบว่าการเรียกใช้ฟังก์ชันนิพจน์แลมบ์ดา fn ไม่รับอาร์กิวเมนต์ใดๆ เนื่องจากอาร์กิวเมนต์ได้รับการเข้ารหัสที่ส่วนท้ายของนิยามฟังก์ชันแลมบ์ดาแล้ว
บทสรุป
นิพจน์แลมบ์ดาเป็นฟังก์ชันที่ไม่ระบุชื่อ มันอยู่ในสองส่วน: คลาสและวัตถุ คำจำกัดความของมันคือคลาส เมื่อนิพจน์ถูกเรียก วัตถุจะถูกสร้างขึ้นจากคำจำกัดความ วัตถุนี้เรียกว่าการปิด มันคือการปิดที่ทำงานที่แลมบ์ดาคาดว่าจะทำ
เพื่อให้นิพจน์แลมบ์ดารับตัวแปรจากขอบเขตฟังก์ชันภายนอก จำเป็นต้องมีส่วนคำสั่งการดักจับที่ไม่ว่างลงในเนื้อหาของฟังก์ชัน