นิพจน์แลมบ์ดาใน C++

Lambda Expressions C



ทำไมต้องใช้ 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

ในโปรแกรมต่อไปนี้ ฟังก์ชันซึ่งเป็นนิพจน์แลมบ์ดาถูกกำหนดให้กับตัวแปร:





#รวม

โดยใช้ เนมสเปซชั่วโมง;

รถยนต์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, A

6, 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 ไม่รับอาร์กิวเมนต์ใดๆ เนื่องจากอาร์กิวเมนต์ได้รับการเข้ารหัสที่ส่วนท้ายของนิยามฟังก์ชันแลมบ์ดาแล้ว

บทสรุป

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

เพื่อให้นิพจน์แลมบ์ดารับตัวแปรจากขอบเขตฟังก์ชันภายนอก จำเป็นต้องมีส่วนคำสั่งการดักจับที่ไม่ว่างลงในเนื้อหาของฟังก์ชัน