แลมบ์ดาแสดงออกใน C ++ 11 คืออะไร? ฉันจะใช้เมื่อใด ปัญหาประเภทใดที่พวกเขาแก้ได้ที่ไม่สามารถทำได้ก่อนการเปิดตัว?
ตัวอย่างเล็ก ๆ น้อย ๆ และการใช้เคสจะมีประโยชน์
แลมบ์ดาแสดงออกใน C ++ 11 คืออะไร? ฉันจะใช้เมื่อใด ปัญหาประเภทใดที่พวกเขาแก้ได้ที่ไม่สามารถทำได้ก่อนการเปิดตัว?
ตัวอย่างเล็ก ๆ น้อย ๆ และการใช้เคสจะมีประโยชน์
คำตอบ:
C ++ มีฟังก์ชั่นทั่วไปที่มีประโยชน์เช่นstd::for_each
และstd::transform
ซึ่งมีประโยชน์มาก น่าเสียดายที่มันยังค่อนข้างยุ่งยากในการใช้งานโดยเฉพาะอย่างยิ่งถ้าfunctor ที่คุณต้องการนำไปใช้นั้นไม่ซ้ำกับฟังก์ชันเฉพาะ
#include <algorithm>
#include <vector>
namespace {
struct f {
void operator()(int) {
// do something
}
};
}
void func(std::vector<int>& v) {
f f;
std::for_each(v.begin(), v.end(), f);
}
หากคุณใช้เพียงf
ครั้งเดียวและในสถานที่เฉพาะดูเหมือนว่า overkill จะเขียนทั้งชั้นเพียงเพื่อทำสิ่งเล็กน้อยและปิด
ใน C ++ 03 คุณอาจถูกล่อลวงให้เขียนบางอย่างเช่นต่อไปนี้เพื่อให้นักเขียนท้องถิ่น:
void func2(std::vector<int>& v) {
struct {
void operator()(int) {
// do something
}
} f;
std::for_each(v.begin(), v.end(), f);
}
อย่างไรก็ตามสิ่งนี้ไม่ได้รับอนุญาตf
ไม่สามารถส่งผ่านไปยังฟังก์ชันเทมเพลตใน C ++ 03
C ++ 11 แนะนำ lambdas ช่วยให้คุณสามารถเขียนแบบอินไลน์ functor struct f
ที่ไม่ระบุชื่อที่จะเปลี่ยน สำหรับตัวอย่างง่ายๆขนาดเล็กสิ่งนี้สามารถอ่านได้ง่ายขึ้น (เก็บทุกอย่างไว้ในที่เดียว) และอาจง่ายต่อการบำรุงรักษาตัวอย่างเช่นในรูปแบบที่ง่ายที่สุด:
void func3(std::vector<int>& v) {
std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}
ฟังก์ชั่นของแลมบ์ดานั้นเป็นเพียงน้ำตาลซินแท็กซ์
ในกรณีง่ายประเภทการคืนแลมบ์ดาจะอนุมานสำหรับคุณเช่น:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) { return d < 0.00001 ? 0 : d; }
);
}
อย่างไรก็ตามเมื่อคุณเริ่มเขียน lambdas ที่ซับซ้อนมากขึ้นคุณจะพบกับกรณีที่ประเภทการคืนค่าไม่สามารถสรุปได้โดยคอมไพเลอร์เช่น:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
ในการแก้ไขปัญหานี้คุณได้รับอนุญาตให้ระบุประเภทส่งคืนสำหรับฟังก์ชัน lambda อย่างชัดเจนโดยใช้-> T
:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) -> double {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
จนถึงตอนนี้เราไม่ได้ใช้อะไรนอกจากสิ่งที่ถูกส่งผ่านไปยังแลมบ์ดาภายใน แต่เรายังสามารถใช้ตัวแปรอื่น ๆ ได้ภายในแลมบ์ดา หากคุณต้องการเข้าถึงตัวแปรอื่น ๆ คุณสามารถใช้การจับภาพประโยค ( []
ของการแสดงออก) ซึ่งยังไม่ได้ใช้ในตัวอย่างเหล่านี้เช่น:
void func5(std::vector<double>& v, const double& epsilon) {
std::transform(v.begin(), v.end(), v.begin(),
[epsilon](double d) -> double {
if (d < epsilon) {
return 0;
} else {
return d;
}
});
}
คุณสามารถจับภาพได้ทั้งการอ้างอิงและค่าซึ่งคุณสามารถระบุการใช้&
และ=
ตามลำดับ:
[&epsilon]
จับโดยอ้างอิง[&]
รวบรวมตัวแปรทั้งหมดที่ใช้ในแลมบ์ดาโดยการอ้างอิง[=]
รวบรวมตัวแปรทั้งหมดที่ใช้ในแลมบ์ดาตามค่า[&, epsilon]
จับตัวแปรเช่นเดียวกับ [&] แต่ epsilon ตามค่า[=, &epsilon]
จับตัวแปรเช่นเดียวกับ [=] แต่ epsilon โดยการอ้างอิงที่สร้างขึ้นoperator()
เป็นconst
ค่าเริ่มต้นโดยมีความหมายที่จะจับconst
เมื่อคุณเข้าถึงได้โดยค่าเริ่มต้น นี้มีผลกระทบที่โทรด้วยการป้อนข้อมูลเดียวกันจะสร้างผลเดียวกันแต่ละ แต่คุณสามารถทำเครื่องหมายแลมบ์ดาเป็นmutable
คำขอของว่าที่ผลิตไม่ได้operator()
const
const
เสมอ ...
()
- มันถูกส่งผ่านเป็นแลมบ์ดาอาร์กิวเมนต์ที่เป็นศูนย์ แต่เนื่องจาก() const
ไม่ตรงกับแลมบ์ดามันจึงมองหาการแปลงแบบที่อนุญาตให้ใช้ -to-function-pointer แล้วเรียกมันว่า! ส่อเสียด!
std::function<double(int, bool)> f = [](int a, bool b) -> double { ... };
แต่โดยปกติเราจะให้คอมไพเลอร์อนุมานประเภท: auto f = [](int a, bool b) -> double { ... };
(และอย่าลืม#include <functional>
)
return d < 0.00001 ? 0 : d;
ถูกรับประกันให้คืนสองเท่าเมื่อหนึ่งในตัวถูกดำเนินการเป็นค่าคงที่จำนวนเต็ม (เป็นเพราะกฎการส่งเสริมโดยนัยของ?: ผู้ประกอบการที่ตัวถูกดำเนินการที่สองและสามมีความสมดุลต่อกันผ่านเลขคณิตปกติ การแปลงไม่ว่าจะเลือกรายการใด) การเปลี่ยนไปใช้0.0 : d
อาจทำให้ตัวอย่างเข้าใจได้ง่ายขึ้น
แนวคิดของซีพลัสพลัสของฟังก์ชั่นแลมบ์ดาเกิดขึ้นในแคลคูลัสแลมบ์ดาและการโปรแกรมเชิงฟังก์ชัน แลมบ์ดาเป็นฟังก์ชั่นที่ไม่มีชื่อที่มีประโยชน์ (ในการเขียนโปรแกรมจริงไม่ใช่ทฤษฎี) สำหรับตัวอย่างสั้น ๆ ของรหัสที่เป็นไปไม่ได้ที่จะนำมาใช้ซ้ำและไม่คุ้มค่าที่จะตั้งชื่อ
ใน C ++ ฟังก์ชัน lambda จะถูกกำหนดเช่นนี้
[]() { } // barebone lambda
หรือในรัศมีภาพทั้งหมด
[]() mutable -> T { } // T is the return type, still lacking throw()
[]
เป็นรายการการดักจับรายการ()
อาร์กิวเมนต์และ{}
เนื้อหาของฟังก์ชัน
รายการการดักจับกำหนดสิ่งที่มาจากด้านนอกของแลมบ์ดาที่ควรมีอยู่ในร่างกายของฟังก์ชั่นและวิธีการ มันอาจเป็นได้ทั้ง:
คุณสามารถผสมใด ๆ [x, &y]
ข้างต้นในเครื่องหมายจุลภาคคั่นรายการ
รายการอาร์กิวเมนต์เหมือนกับในฟังก์ชัน C ++ อื่น ๆ
รหัสที่จะถูกดำเนินการเมื่อแลมบ์ดาถูกเรียกจริง
decltype(return_statement)
ถ้าแลมบ์ดามีเพียงคำสั่งคืนหนึ่งพิมพ์กลับสามารถละเว้นและมีชนิดโดยนัยของ
หากแลมบ์ดาทำเครื่องหมายไม่แน่นอน (เช่น[]() mutable { }
) จะอนุญาตให้ทำการเปลี่ยนแปลงค่าที่ถูกบันทึกโดยค่า
ไลบรารี่ที่กำหนดโดยมาตรฐาน ISO ได้รับประโยชน์อย่างมากจาก lambdas และเพิ่มความสามารถในการใช้งานหลายแท่งเนื่องจากตอนนี้ผู้ใช้ไม่จำเป็นต้องยุ่งเหยิงโค้ดด้วยฟังก์ชั่นขนาดเล็กในขอบเขตที่สามารถเข้าถึงได้
ใน C ++ 14 lambdas ได้รับการขยายโดยข้อเสนอต่างๆ
=
องค์ประกอบของรายการจับภาพขณะนี้คุณสามารถเริ่มต้นได้ด้วย สิ่งนี้ช่วยให้การเปลี่ยนชื่อของตัวแปรและการจับโดยการย้าย ตัวอย่างที่นำมาจากมาตรฐาน:
int x = 4;
auto y = [&r = x, x = x+1]()->int {
r += 2;
return x+2;
}(); // Updates ::x to 6, and initializes y to 7.
และอีกภาพหนึ่งนำมาจาก Wikipedia ซึ่งแสดงวิธีจับภาพด้วยstd::move
:
auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};
ตอนนี้แลมบ์ดาอาจเป็นแบบทั่วไป ( auto
จะเทียบเท่ากับT
ที่นี่หาก
T
เป็นอาร์กิวเมนต์เทมเพลตชนิดที่ใดที่หนึ่งในขอบเขตโดยรอบ):
auto lambda = [](auto x, auto y) {return x + y;};
C ++ 14 ช่วยให้กลับประเภทอนุมานได้ทุกฟังก์ชั่นและไม่ได้ จำกัด return expression;
ไว้เฉพาะฟังก์ชั่นของรูปแบบ นี่คือการขยายไปยัง lambdas
r = &x; r += 2;
แต่สิ่งนี้เกิดขึ้นกับค่าดั้งเดิมของ 4
โดยทั่วไปแล้วนิพจน์แลมบ์ดาจะใช้ห่อหุ้มอัลกอริทึมเพื่อให้สามารถส่งผ่านไปยังฟังก์ชันอื่นได้ แต่ก็เป็นไปได้ที่จะดำเนินการแลมบ์ดาทันทีที่คำนิยาม :
[&](){ ...your code... }(); // immediately executed lambda expression
เทียบเท่ากับหน้าที่
{ ...your code... } // simple code block
สิ่งนี้ทำให้แลมบ์ดานิพจน์เป็นเครื่องมือที่ทรงพลังสำหรับการทำหน้าที่ซับซ้อนอีกครั้ง คุณเริ่มต้นด้วยการตัดส่วนของรหัสในฟังก์ชั่นแลมบ์ดาตามที่แสดงไว้ด้านบน กระบวนการของการกำหนดพารามิเตอร์ที่ชัดเจนนั้นสามารถดำเนินการได้ค่อย ๆ ด้วยการทดสอบระดับกลางหลังจากแต่ละขั้นตอน เมื่อคุณมีการบล็อกพารามิเตอร์อย่างเต็มรูปแบบ (ดังแสดงโดยการลบ&
) คุณสามารถย้ายรหัสไปยังตำแหน่งภายนอกและทำให้มันเป็นฟังก์ชั่นปกติ
ในทำนองเดียวกันคุณสามารถใช้แลมบ์ดานิพจน์เพื่อเริ่มต้นตัวแปรตามผลลัพธ์ของอัลกอริทึม ...
int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!
เป็นวิธีการแบ่งตรรกะโปรแกรมของคุณคุณอาจพบว่ามีประโยชน์ในการส่งผ่านการแสดงออกแลมบ์ดาเป็นอาร์กิวเมนต์เพื่อการแสดงออกแลมบ์ดาอื่น ...
[&]( std::function<void()> algorithm ) // wrapper section
{
...your wrapper code...
algorithm();
...your wrapper code...
}
([&]() // algorithm section
{
...your algorithm code...
});
นิพจน์แลมบ์ดาให้คุณสร้างฟังก์ชั่นที่ซ้อนกันซึ่งมีชื่อซึ่งสามารถเป็นวิธีที่สะดวกในการหลีกเลี่ยงตรรกะที่ซ้ำกัน การใช้ lambdas ชื่อนั้นมีแนวโน้มที่จะเป็นเรื่องง่ายขึ้นเล็กน้อยในสายตา (เมื่อเทียบกับ lambdas แบบอินไลน์ที่ไม่ระบุชื่อ) เมื่อผ่านฟังก์ชั่นที่ไม่สำคัญเป็นพารามิเตอร์ไปยังฟังก์ชันอื่น หมายเหตุ: อย่าลืมเครื่องหมายอัฒภาคหลังจากวงเล็บปีกกาปิด
auto algorithm = [&]( double x, double m, double b ) -> double
{
return m*x+b;
};
int a=algorithm(1,2,3), b=algorithm(4,5,6);
หากการทำโปรไฟล์ในภายหลังแสดงให้เห็นว่าค่าใช้จ่ายในการเริ่มต้นที่สำคัญสำหรับวัตถุฟังก์ชั่นคุณอาจเลือกที่จะเขียนสิ่งนี้เป็นฟังก์ชั่นปกติ
if
แถลงการณ์: if ([i]{ for (char j : i) if (!isspace(j)) return false ; return true ; }()) // i is all whitespace
สมมติว่าi
เป็นstd::string
[](){}();
ดังนั้นต่อไปนี้คือการแสดงออกทางกฎหมาย:
(lambda: None)()
ไวยากรณ์ของไพ ธ อนชัดเจนมากขึ้น
main() {{{{((([](){{}}())));}}}}
คำตอบ
ถาม: แลมบ์ดานิพจน์คืออะไรใน C ++ 11
A: ภายใต้ฝากระโปรงก็เป็นวัตถุของชั้น autogenerated มีมากไปประกอบการ () const วัตถุดังกล่าวเรียกว่าการปิดและสร้างโดยคอมไพเลอร์ แนวคิด 'การปิด' นี้อยู่ใกล้กับแนวคิดการเชื่อมโยงจาก C ++ 11 แต่โดยทั่วไปแลมบ์ดาจะสร้างรหัสที่ดีกว่า และการโทรผ่านการปิดช่วยให้มีการฝังเต็ม
ถาม: ฉันจะใช้เมื่อใด
ตอบ: เพื่อกำหนด "ตรรกะที่ง่ายและเล็ก" และขอให้คอมไพเลอร์ทำการสร้างจากคำถามก่อนหน้า คุณให้คอมไพเลอร์นิพจน์บางอย่างที่คุณต้องการให้อยู่ภายในตัวดำเนินการ () คอมไพเลอร์สิ่งอื่น ๆ ทั้งหมดจะสร้างให้คุณ
ถาม: ปัญหาประเภทใดที่พวกเขาแก้ไขซึ่งไม่สามารถทำได้ก่อนการเปิดตัว?
ตอบ: มันเป็นรูปแบบของไวยากรณ์ที่คล้ายกับตัวดำเนินการโอเวอร์โหลดแทนฟังก์ชันสำหรับการเพิ่มแบบกำหนดเอง,การดำเนินการsubrtact ... แต่มันจะบันทึกบรรทัดที่ไม่จำเป็นของโค้ดเพิ่มเติมเพื่อตัดตรรกะ 1-3 บรรทัดของคลาสจริงและบางคลาส! วิศวกรบางคนคิดว่าถ้าจำนวนบรรทัดน้อยลงมีโอกาสน้อยที่จะทำผิดพลาดได้ (ฉันก็คิดเช่นนั้น)
ตัวอย่างการใช้งาน
auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);
ความพิเศษเกี่ยวกับ lambdas, ไม่ครอบคลุมโดยคำถาม ละเว้นหัวข้อนี้หากคุณไม่สนใจ
1. ค่าที่บันทึกไว้ คุณสามารถจับภาพอะไรได้บ้าง
1.1 คุณสามารถอ้างถึงตัวแปรที่มีระยะเวลาการจัดเก็บแบบคงที่ใน lambdas พวกเขาทั้งหมดถูกจับ
1.2 คุณสามารถใช้แลมบ์ดาสำหรับการจับภาพค่า "ตามค่า" ในกรณีดังกล่าว vars ที่จับจะถูกคัดลอกไปยังวัตถุฟังก์ชัน (ปิด)
[captureVar1,captureVar2](int arg1){}
1.3 คุณสามารถจับได้อ้างอิง & - ในบริบทนี้หมายถึงการอ้างอิงไม่ใช่ตัวชี้
[&captureVar1,&captureVar2](int arg1){}
1.4 มันมีสัญกรณ์ที่จะจับ vars ที่ไม่คงที่ทั้งหมดตามค่าหรือโดยการอ้างอิง
[=](int arg1){} // capture all not-static vars by value
[&](int arg1){} // capture all not-static vars by reference
1.5 มันมีสัญกรณ์ที่จะจับ vars ที่ไม่คงที่ทั้งหมดตามค่าหรือโดยการอ้างอิงและระบุ smth มากกว่า. ตัวอย่าง: เก็บ vars แบบไม่คงที่ทั้งหมดตามค่า แต่โดยการจับการอ้างอิง Param2
[=,&Param2](int arg1){}
เก็บ vars ที่ไม่คงที่ทั้งหมดโดยการอ้างอิง แต่โดยการจับค่า Param2
[&,Param2](int arg1){}
2. ผลตอบแทนประเภทหัก
2.1 สามารถคืนค่าชนิดแลมบ์ดาได้หากแลมบ์ดาเป็นหนึ่งนิพจน์ หรือคุณสามารถระบุได้อย่างชัดเจน
[=](int arg1)->trailing_return_type{return trailing_return_type();}
หากแลมบ์ดามีมากกว่าหนึ่งนิพจน์แล้วจะต้องระบุชนิดส่งคืนโดยใช้ชนิดการส่งคืนต่อท้าย นอกจากนี้ยังสามารถใช้ไวยากรณ์ที่คล้ายกันกับฟังก์ชั่นอัตโนมัติและฟังก์ชั่นสมาชิก
3. ค่าที่บันทึกไว้ สิ่งที่คุณไม่สามารถจับได้
3.1 คุณสามารถจับเฉพาะ vars เฉพาะที่ไม่ใช่ตัวแปรสมาชิกของวัตถุ
4. Сonversions
4.1 !! แลมบ์ดาไม่ได้เป็นตัวชี้ฟังก์ชั่นและไม่ใช่ฟังก์ชั่นที่ไม่ระบุตัวตน แต่ลูกแกะที่ไม่มีการจับภาพสามารถแปลงเป็นตัวชี้ฟังก์ชั่นได้
PS
ข้อมูลเพิ่มเติมเกี่ยวกับข้อมูลไวยากรณ์แลมบ์ดาสามารถพบได้ใน Working draft สำหรับการเขียนโปรแกรมภาษา C ++ # 337, 2012-01-16, 5.1.2 แลมบ์ดานิพจน์หน้า 88
ใน C ++ 14 มีการเพิ่มฟีเจอร์พิเศษที่มีชื่อว่า "init capture" อนุญาตให้ทำการประกาศข้อมูลการปิดสมาชิกโดยพลการ:
auto toFloat = [](int value) { return float(value);};
auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};
[&,=Param2](int arg1){}
ดูเหมือนว่านี่จะไม่ใช่ไวยากรณ์ที่ถูกต้อง แบบฟอร์มที่ถูกต้องคือ[&,Param2](int arg1){}
ฟังก์ชั่นแลมบ์ดาเป็นฟังก์ชั่นนิรนามที่คุณสร้างในบรรทัด สามารถจับตัวแปรตามที่อธิบายไว้ (เช่นhttp://www.stroustrup.com/C++11FAQ.html#lambda ) แต่มีข้อ จำกัด บางประการ ตัวอย่างเช่นหากมีอินเทอร์เฟซการติดต่อกลับเช่นนี้
void apply(void (*f)(int)) {
f(10);
f(20);
f(30);
}
คุณสามารถเขียนฟังก์ชั่นตรงจุดเพื่อใช้งานได้เหมือนกับฟังก์ชั่นที่ส่งผ่านเพื่อใช้งานด้านล่าง:
int col=0;
void output() {
apply([](int data) {
cout << data << ((++col % 10) ? ' ' : '\n');
});
}
แต่คุณไม่สามารถทำสิ่งนี้:
void output(int n) {
int col=0;
apply([&col,n](int data) {
cout << data << ((++col % 10) ? ' ' : '\n');
});
}
เนื่องจากข้อ จำกัด ในมาตรฐาน C ++ 11 หากคุณต้องการใช้การจับคุณต้องพึ่งพาห้องสมุดและ
#include <functional>
(หรือไลบรารี STL อื่น ๆ เช่นอัลกอริทึมเพื่อให้ได้ทางอ้อม) จากนั้นทำงานกับ std :: function แทนที่จะส่งผ่านฟังก์ชั่นปกติเป็นพารามิเตอร์ดังนี้:
#include <functional>
void apply(std::function<void(int)> f) {
f(10);
f(20);
f(30);
}
void output(int width) {
int col;
apply([width,&col](int data) {
cout << data << ((++col % width) ? ' ' : '\n');
});
}
apply
เป็นเทมเพลตที่ยอมรับ functor มันจะใช้ได้
หนึ่งในคำอธิบายที่ดีที่สุดที่lambda expression
ได้รับจากผู้เขียน C ++ Bjarne Stroustrupในหนังสือของเขา***The C++ Programming Language***
บทที่ 11 ( ISBN-13: 978-0321563842 ):
What is a lambda expression?
แสดงออกแลมบ์ดา , บางครั้งก็เรียกว่าแลมบ์ดา ฟังก์ชันหรือ (อย่างเคร่งครัดพูดไม่ถูกต้อง แต่เรียกขาน) เป็น แลมบ์ดาเป็นสัญกรณ์ง่ายสำหรับการกำหนดและการใช้วัตถุฟังก์ชั่นที่ไม่ระบุชื่อ แทนที่จะกำหนดคลาสที่มีชื่อด้วยโอเปอเรเตอร์ () หลังจากนั้นสร้างวัตถุของคลาสนั้นและในที่สุดก็เรียกใช้มันเราสามารถใช้ชวเลข
When would I use one?
สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อเราต้องการผ่านการดำเนินการเป็นอาร์กิวเมนต์ไปยังอัลกอริทึม ในบริบทของอินเตอร์เฟซผู้ใช้แบบกราฟิก (และอื่น ๆ ) การดำเนินงานดังกล่าวมักจะเรียกว่าการเรียกกลับ
What class of problem do they solve that wasn't possible prior to their introduction?
ที่นี่ฉันเดาว่าทุกการกระทำที่กระทำด้วยการแสดงออกแลมบ์ดาสามารถแก้ไขได้หากไม่มีพวกเขา แต่ด้วยรหัสที่มากขึ้นและความซับซ้อนที่ใหญ่กว่ามาก การแสดงออกของแลมบ์ดานี่เป็นวิธีการเพิ่มประสิทธิภาพสำหรับโค้ดของคุณและวิธีการทำให้มันน่าสนใจยิ่งขึ้น เศร้าโดย Stroustup:
วิธีที่มีประสิทธิภาพของการเพิ่มประสิทธิภาพ
Some examples
ผ่านการแสดงออกแลมบ์ดา
void print_modulo(const vector<int>& v, ostream& os, int m) // output v[i] to os if v[i]%m==0
{
for_each(begin(v),end(v),
[&os,m](int x) {
if (x%m==0) os << x << '\n';
});
}
หรือผ่านฟังก์ชั่น
class Modulo_print {
ostream& os; // members to hold the capture list int m;
public:
Modulo_print(ostream& s, int mm) :os(s), m(mm) {}
void operator()(int x) const
{
if (x%m==0) os << x << '\n';
}
};
หรือแม้กระทั่ง
void print_modulo(const vector<int>& v, ostream& os, int m)
// output v[i] to os if v[i]%m==0
{
class Modulo_print {
ostream& os; // members to hold the capture list
int m;
public:
Modulo_print (ostream& s, int mm) :os(s), m(mm) {}
void operator()(int x) const
{
if (x%m==0) os << x << '\n';
}
};
for_each(begin(v),end(v),Modulo_print{os,m});
}
หากคุณต้องการคุณสามารถชื่อlambda expression
เช่นด้านล่าง:
void print_modulo(const vector<int>& v, ostream& os, int m)
// output v[i] to os if v[i]%m==0
{
auto Modulo_print = [&os,m] (int x) { if (x%m==0) os << x << '\n'; };
for_each(begin(v),end(v),Modulo_print);
}
หรือสมมติอีกตัวอย่างง่ายๆ
void TestFunctions::simpleLambda() {
bool sensitive = true;
std::vector<int> v = std::vector<int>({1,33,3,4,5,6,7});
sort(v.begin(),v.end(),
[sensitive](int x, int y) {
printf("\n%i\n", x < y);
return sensitive ? x < y : abs(x) < abs(y);
});
printf("sorted");
for_each(v.begin(), v.end(),
[](int x) {
printf("x - %i;", x);
}
);
}
จะสร้างต่อไป
0
1
0
1
0
1
0
1
0
1
0 sortx - 1; x - 3; x - 4; x - 5; x - 6; x - 7; x - 33;
[]
- นี่คือรายการบันทึกหรือlambda introducer
: หากlambdas
ไม่ต้องการเข้าถึงสภาพแวดล้อมในท้องถิ่นของเราเราสามารถใช้งานได้
อ้างอิงจากหนังสือ:
ตัวอักษรตัวแรกของการแสดงออกแลมบ์ดาอยู่เสมอ[ ผู้แนะนำแลมบ์ดาสามารถทำได้หลายรูปแบบ:
• [] : รายการจับภาพเปล่า นี่ก็หมายความว่าไม่มีชื่อท้องถิ่นจากบริบทโดยรอบสามารถนำมาใช้ในร่างกายแลมบ์ดา สำหรับการแสดงออกแลมบ์ดาดังกล่าวข้อมูลจะได้รับจากการขัดแย้งหรือจากตัวแปรที่ไม่ใช่ท้องถิ่น
• [&] : การจับโดยนัยโดยการอ้างอิง ชื่อท้องถิ่นทั้งหมดสามารถใช้ได้ ตัวแปรท้องถิ่นทั้งหมดเข้าถึงได้โดยอ้างอิง
• [=] : จับค่าโดยปริยาย ชื่อท้องถิ่นทั้งหมดสามารถใช้ได้ ชื่อทั้งหมดอ้างถึงสำเนาของตัวแปรโลคัลที่ถ่าย ณ จุดที่เรียกใช้นิพจน์แลมบ์ดา
• [รายการจับภาพ]: การ จับภาพที่ชัดเจน; รายการจับภาพคือรายการของชื่อของตัวแปรท้องถิ่นที่จะจับ (เช่นเก็บไว้ในวัตถุ) โดยการอ้างอิงหรือตามค่า ตัวแปรที่มีชื่อนำหน้า & ถูกจับโดยการอ้างอิง ตัวแปรอื่น ๆ จะถูกบันทึกโดยค่า รายการจับภาพสามารถมีสิ่งนี้และชื่อตามด้วย ... เป็นองค์ประกอบ
• [&, capture-list] : การจับโดยปริยายโดยอ้างอิงตัวแปรท้องถิ่นทั้งหมดที่มีชื่อที่ไม่ได้กำหนดไว้ในรายการ รายการจับภาพสามารถมีสิ่งนี้ รายชื่อไม่สามารถนำหน้าด้วย & ตัวแปรที่มีชื่อในรายการจับภาพถูกจับโดยค่า
• [=, capture-list] : การจับโดยปริยายตามค่าตัวแปรท้องถิ่นทั้งหมดที่มีชื่อที่ไม่ได้กล่าวถึงในรายการ รายการจับภาพไม่สามารถมีสิ่งนี้ ชื่อที่ปรากฏจะต้องนำหน้าด้วย & ตัวแปรที่มีชื่อในรายการจับภาพถูกจับโดยการอ้างอิง
โปรดทราบว่าชื่อท้องถิ่นที่นำหน้าด้วย & ถูกจับภาพเสมอโดยการอ้างอิงและชื่อท้องถิ่นที่ไม่ได้ถูกจัดเตรียมไว้ล่วงหน้าโดย & จะถูกบันทึกโดยค่าเสมอ การดักจับโดยการอ้างอิงเท่านั้นอนุญาตให้แก้ไขตัวแปรในสภาพแวดล้อมการโทร
Additional
Lambda expression
รูปแบบ
การอ้างอิงเพิ่มเติม:
for (int x : v) { if (x % m == 0) os << x << '\n';}
การใช้งานจริงอย่างหนึ่งที่ฉันได้พบคือการลดรหัสแผ่นบอยเลอร์ ตัวอย่างเช่น:
void process_z_vec(vector<int>& vec)
{
auto print_2d = [](const vector<int>& board, int bsize)
{
for(int i = 0; i<bsize; i++)
{
for(int j=0; j<bsize; j++)
{
cout << board[bsize*i+j] << " ";
}
cout << "\n";
}
};
// Do sth with the vec.
print_2d(vec,x_size);
// Do sth else with the vec.
print_2d(vec,y_size);
//...
}
หากไม่มีแลมบ์ดาคุณอาจต้องทำบางสิ่งเพื่อbsize
กรณีที่แตกต่างกัน แน่นอนคุณสามารถสร้างฟังก์ชั่น แต่ถ้าคุณต้องการ จำกัด การใช้งานภายในขอบเขตของฟังก์ชั่นผู้ใช้วิญญาณ? ธรรมชาติของแลมบ์ดาตอบสนองความต้องการนี้และฉันใช้มันสำหรับกรณีนั้น
แลมบ์ดาใน c ++ ถือเป็น "ในฟังก์ชั่นที่พร้อมใช้งาน" ใช่มันเป็นไปได้อย่างแท้จริงคุณกำหนดมัน; ใช้มัน; และเมื่อขอบเขตฟังก์ชันพาเรนต์เสร็จสิ้นฟังก์ชันแลมบ์ดาจะหายไป
c ++ นำมาใช้ใน c ++ 11 และทุกคนเริ่มใช้มันในทุกที่ที่เป็นไปได้ ตัวอย่างและแลมบ์ดาสามารถพบได้ที่นี่https://en.cppreference.com/w/cpp/language/lambda
ฉันจะอธิบายที่ไม่ได้มี แต่จำเป็นต้องรู้สำหรับโปรแกรมเมอร์ c ++ ทุกคน
แลมบ์ดาไม่ได้ใช้งานทุกที่และทุกฟังก์ชั่นไม่สามารถแทนที่ด้วยแลมบ์ดาได้ มันไม่ได้เร็วที่สุดเมื่อเทียบกับฟังก์ชั่นปกติ เพราะมันมีค่าโสหุ้ยที่ต้องจัดการโดยแลมบ์ดา
มันจะช่วยในการลดจำนวนบรรทัดในบางกรณีอย่างแน่นอน โดยทั่วไปสามารถใช้สำหรับส่วนของรหัสซึ่งได้รับการเรียกใช้ในฟังก์ชั่นเดียวกันหนึ่งครั้งหรือมากกว่านั้นและไม่จำเป็นต้องใช้ชิ้นส่วนของรหัสอื่นที่ใดก็ได้เพื่อให้คุณสามารถสร้างฟังก์ชันแบบสแตนด์อโลนได้
ด้านล่างเป็นตัวอย่างพื้นฐานของแลมบ์ดาและสิ่งที่เกิดขึ้นในพื้นหลัง
รหัสผู้ใช้:
int main()
{
// Lambda & auto
int member=10;
auto endGame = [=](int a, int b){ return a+b+member;};
endGame(4,5);
return 0;
}
วิธีการรวบรวมขยาย:
int main()
{
int member = 10;
class __lambda_6_18
{
int member;
public:
inline /*constexpr */ int operator()(int a, int b) const
{
return a + b + member;
}
public: __lambda_6_18(int _member)
: member{_member}
{}
};
__lambda_6_18 endGame = __lambda_6_18{member};
endGame.operator()(4, 5);
return 0;
}
เพื่อที่คุณจะเห็นว่ามันเพิ่มค่าใช้จ่ายชนิดใดเมื่อคุณใช้ ดังนั้นจึงไม่ควรใช้ทุกที่ มันสามารถใช้ในสถานที่ที่พวกเขามีผลบังคับใช้
ปัญหาหนึ่งที่แก้ไขได้: รหัสง่ายกว่าแลมบ์ดาสำหรับการเรียกใช้ตัวสร้างที่ใช้ฟังก์ชันพารามิเตอร์เอาต์พุตสำหรับการเริ่มต้นสมาชิก const
คุณสามารถเริ่มต้นสมาชิก const ของคลาสของคุณด้วยการเรียกใช้ฟังก์ชันที่ตั้งค่าโดยส่งกลับค่าเป็นพารามิเตอร์เอาต์พุต