Ekspresi Lambda dalam C++

Lambda Expressions C



Mengapa Ekspresi Lambda?

Perhatikan pernyataan berikut:

ke dalammyInt= 52;

Di sini, myInt adalah pengidentifikasi, nilai. 52 adalah literal, sebuah nilai. Hari ini, dimungkinkan untuk mengkodekan fungsi secara khusus dan meletakkannya di posisi 52. Fungsi seperti itu disebut ekspresi lambda. Simak juga program singkat berikut ini:







#termasuk

menggunakan ruang namajam;

ke dalamfn(ke dalammelalui)

{

ke dalammenjawab=melalui+ 3;

kembalimenjawab;

}


ke dalamutama()

{

fn(5);



kembali 0;

}

Hari ini, dimungkinkan untuk mengkodekan fungsi secara khusus dan meletakkannya di posisi argumen 5, dari pemanggilan fungsi, fn(5). Fungsi seperti itu disebut ekspresi lambda. Ekspresi lambda (fungsi) di posisi itu adalah nilai.



Setiap literal kecuali string literal adalah prvalue. Ekspresi lambda adalah desain fungsi khusus yang cocok sebagai literal dalam kode. Ini adalah fungsi anonim (tidak disebutkan namanya). Artikel ini menjelaskan ekspresi utama C++ baru, yang disebut ekspresi lambda. Pengetahuan dasar dalam C++ adalah syarat untuk memahami artikel ini.



Isi Artikel

Ilustrasi Ekspresi Lambda

Dalam program berikut, sebuah fungsi, yang merupakan ekspresi lambda, ditugaskan ke variabel:





#termasuk

menggunakan ruang namajam;

mobilfn= [](ke dalamberhenti)

{

ke dalammenjawab=berhenti+ 3;

kembalimenjawab;

};


ke dalamutama()

{

mobilvariasi=fn(2);

biaya <<variasi<< ' ';


kembali 0;

}

Outputnya adalah:

5

Di luar fungsi main(), ada variabel, fn. Jenisnya adalah otomatis. Otomatis dalam situasi ini berarti bahwa tipe aktual, seperti int atau float, ditentukan oleh operan kanan dari operator penugasan (=). Di sebelah kanan operator penugasan adalah ekspresi lambda. Ekspresi lambda adalah fungsi tanpa tipe pengembalian sebelumnya. Perhatikan penggunaan dan posisi tanda kurung siku, []. Fungsi mengembalikan 5, sebuah int, yang akan menentukan tipe untuk fn.



Dalam fungsi main(), ada pernyataan:

mobilvariasi=fn(2);

Ini berarti, fn di luar main(), berakhir sebagai pengidentifikasi untuk suatu fungsi. Parameter implisitnya adalah parameter dari ekspresi lambda. Jenis untuk variab adalah otomatis.

Perhatikan bahwa ekspresi lambda diakhiri dengan titik koma, seperti definisi kelas atau struct, diakhiri dengan titik koma.

Dalam program berikut, sebuah fungsi, yang merupakan ekspresi lambda yang mengembalikan nilai 5, adalah argumen ke fungsi lain:

#termasuk

menggunakan ruang namajam;

ruang kosonglainfn(ke dalamtidak1,ke dalam (*ptr)(ke dalam))

{

ke dalamno2= (*ptr)(2);

biaya <<no1<< '' <<no2<< ' ';

}


ke dalamutama()

{

lainfn(4,[](ke dalamberhenti)

{

ke dalammenjawab=berhenti+ 3;

kembalimenjawab;

});


kembali 0;
}

Outputnya adalah:

Empat. Lima

Ada dua fungsi di sini, ekspresi lambda dan fungsi otherfn(). Ekspresi lambda adalah argumen kedua dari otherfn(), yang disebut main(). Perhatikan bahwa fungsi lambda (ekspresi) tidak diakhiri dengan titik koma dalam panggilan ini karena, di sini, ini adalah argumen (bukan fungsi yang berdiri sendiri).

Parameter fungsi lambda dalam definisi fungsi otherfn() adalah penunjuk ke suatu fungsi. Pointer memiliki nama, ptr. Nama, ptr, digunakan dalam definisi otherfn() untuk memanggil fungsi lambda.

Pernyataan,

ke dalamno2= (*ptr)(2);

Dalam definisi otherfn(), ia memanggil fungsi lambda dengan argumen 2. Nilai kembalian dari panggilan, '(*ptr)(2)' dari fungsi lambda, ditetapkan ke no2.

Program di atas juga menunjukkan bagaimana fungsi lambda dapat digunakan dalam skema fungsi callback C++.

Bagian dari Ekspresi Lambda

Bagian-bagian dari fungsi lambda yang khas adalah sebagai berikut:

[] () {}
  • [] adalah klausa penangkapan. Itu bisa memiliki item.
  • () adalah untuk daftar parameter.
  • {} adalah untuk badan fungsi. Jika fungsinya berdiri sendiri, maka harus diakhiri dengan titik koma.

Tangkapan

Definisi fungsi lambda dapat ditetapkan ke variabel atau digunakan sebagai argumen untuk pemanggilan fungsi yang berbeda. Definisi untuk pemanggilan fungsi seperti itu harus memiliki parameter, penunjuk ke suatu fungsi, yang sesuai dengan definisi fungsi lambda.

Definisi fungsi lambda berbeda dari definisi fungsi normal. Itu dapat ditugaskan ke variabel dalam lingkup global; fungsi-ditugaskan-ke-variabel ini juga dapat dikodekan di dalam fungsi lain. Saat ditugaskan ke variabel lingkup global, tubuhnya dapat melihat variabel lain dalam lingkup global. Ketika ditugaskan ke variabel di dalam definisi fungsi normal, tubuhnya dapat melihat variabel lain dalam lingkup fungsi hanya dengan bantuan klausa capture, [].

Klausa capture [], juga dikenal sebagai lambda-introducer, memungkinkan variabel dikirim dari lingkup (fungsi) di sekitarnya ke dalam badan fungsi ekspresi lambda. Badan fungsi ekspresi lambda dikatakan menangkap variabel saat menerima objek. Tanpa klausa penangkapan [], variabel tidak dapat dikirim dari lingkup sekitarnya ke badan fungsi ekspresi lambda. Program berikut mengilustrasikan ini, dengan cakupan fungsi main(), sebagai cakupan sekitarnya:

#termasuk

menggunakan ruang namajam;

ke dalamutama()

{

ke dalamIndo= 5;


mobilfn= [Indo]()

{

biaya <<Indo<< ' ';

};

fn();


kembali 0;

}

Keluarannya adalah 5 . Tanpa nama, id, di dalam [], ekspresi lambda tidak akan melihat variabel id dari lingkup fungsi main().

Menangkap dengan Referensi

Contoh penggunaan klausa capture di atas adalah menangkap berdasarkan nilai (lihat detail di bawah). Dalam menangkap dengan referensi, lokasi (penyimpanan) variabel, misalnya, id di atas, dari cakupan sekitarnya, tersedia di dalam badan fungsi lambda. Jadi, mengubah nilai variabel di dalam badan fungsi lambda akan mengubah nilai variabel yang sama di lingkup sekitarnya. Setiap variabel yang diulang dalam klausa capture didahului oleh ampersand (&) untuk mencapai ini. Program berikut menggambarkan hal ini:

#termasuk

menggunakan ruang namajam;

ke dalamutama()

{

ke dalamIndo= 5; mengambangkaki= 2.3; arangch= 'KE';

mobilfn= [&Indo,&kaki,&ch]()

{

Indo= 6;kaki= 3.4;ch= 'B';

};

fn();

biaya <<Indo<< ',' <<kaki<< ',' <<ch<< ' ';

kembali 0;

}

Outputnya adalah:

6, 3.4, B

Mengonfirmasi bahwa nama variabel di dalam badan fungsi ekspresi lambda adalah untuk variabel yang sama di luar ekspresi lambda.

Menangkap berdasarkan Nilai

Dalam menangkap berdasarkan nilai, salinan lokasi variabel, dari cakupan sekitarnya, tersedia di dalam badan fungsi lambda. Meskipun variabel di dalam tubuh fungsi lambda adalah salinan, nilainya tidak dapat diubah di dalam tubuh untuk saat ini. Untuk mencapai penangkapan berdasarkan nilai, setiap variabel yang diulang dalam klausa penangkapan tidak didahului oleh apa pun. Program berikut menggambarkan hal ini:

#termasuk

menggunakan ruang namajam;

ke dalamutama()

{

ke dalamIndo= 5; mengambangkaki= 2.3; arangch= 'KE';

mobilfn= [id, kaki, ch]()

{

//id = 6; kaki = 3,4; ch = 'B';

biaya <<Indo<< ',' <<kaki<< ',' <<ch<< ' ';

};

fn();

Indo= 6;kaki= 3.4;ch= 'B';

biaya <<Indo<< ',' <<kaki<< ',' <<ch<< ' ';

kembali 0;

}

Outputnya adalah:

5, 2.3, A

6, 3.4, B

Jika indikator komentar dihapus, program tidak akan dikompilasi. Kompiler akan mengeluarkan pesan kesalahan bahwa variabel di dalam definisi badan fungsi dari ekspresi lambda tidak dapat diubah. Meskipun variabel tidak dapat diubah di dalam fungsi lambda, variabel tersebut dapat diubah di luar fungsi lambda, seperti yang ditunjukkan oleh output program di atas.

Mencampur Tangkapan

Menangkap berdasarkan referensi dan menangkap berdasarkan nilai dapat dicampur, seperti yang ditunjukkan oleh program berikut:

#termasuk

menggunakan ruang namajam;

ke dalamutama()

{

ke dalamIndo= 5; mengambangkaki= 2.3; arangch= 'KE'; boolbl= benar;


mobilfn= [id, kaki,&ch,&bl]()

{

ch= 'B';bl= Salah;

biaya <<Indo<< ',' <<kaki<< ',' <<ch<< ',' <<bl<< ' ';

};

fn();


kembali 0;

}

Outputnya adalah:

5, 2.3, B, 0

Ketika semua ditangkap, adalah dengan referensi:

Jika semua variabel yang akan ditangkap ditangkap oleh referensi, maka hanya satu & akan cukup dalam klausa penangkapan. Program berikut menggambarkan hal ini:

#termasuk

menggunakan ruang namajam;

ke dalamutama()

{

ke dalamIndo= 5; mengambangkaki= 2.3; arangch= 'KE'; boolbl= benar;


mobilfn= [&]()

{

Indo= 6;kaki= 3.4;ch= 'B';bl= Salah;

};

fn();

biaya <<Indo<< ',' <<kaki<< ',' <<ch<< ',' <<bl<< ' ';


kembali 0;

}

Outputnya adalah:

6, 3.4, B, 0

Jika beberapa variabel akan ditangkap oleh referensi dan yang lain berdasarkan nilai, maka satu & akan mewakili semua referensi, dan sisanya masing-masing tidak akan didahului oleh apa pun, seperti yang ditunjukkan oleh program berikut:

menggunakan ruang namajam;

ke dalamutama()

{

ke dalamIndo= 5; mengambangkaki= 2.3; arangch= 'KE'; boolbl= benar;


mobilfn= [&, id, ft]()

{

ch= 'B';bl= Salah;

biaya <<Indo<< ',' <<kaki<< ',' <<ch<< ',' <<bl<< ' ';

};

fn();


kembali 0;

}

Outputnya adalah:

5, 2.3, B, 0

Perhatikan bahwa & sendiri (yaitu, & tidak diikuti oleh pengenal) harus menjadi karakter pertama dalam klausa tangkap.

Ketika semua ditangkap, berdasarkan nilai:

Jika semua variabel yang akan ditangkap akan ditangkap oleh nilai, maka hanya satu = akan cukup dalam klausa penangkapan. Program berikut menggambarkan hal ini:

#termasuk

menggunakan ruang namajam;

ke dalamutama()
{

ke dalamIndo= 5; mengambangkaki= 2.3; arangch= 'KE'; boolbl= benar;


mobilfn= [=]()

{

biaya <<Indo<< ',' <<kaki<< ',' <<ch<< ',' <<bl<< ' ';

};

fn();


kembali 0;


}

Outputnya adalah:

5, 2.3, A, 1

Catatan : = hanya-baca, sampai sekarang.

Jika beberapa variabel akan ditangkap oleh nilai dan yang lain dengan referensi, maka satu = akan mewakili semua variabel yang disalin hanya-baca, dan sisanya masing-masing akan memiliki &, seperti yang ditunjukkan oleh program berikut:

#termasuk

menggunakan ruang namajam;

ke dalamutama()

{

ke dalamIndo= 5; mengambangkaki= 2.3; arangch= 'KE'; boolbl= benar;


mobilfn= [=,&ch,&bl]()

{

ch= 'B';bl= Salah;

biaya <<Indo<< ',' <<kaki<< ',' <<ch<< ',' <<bl<< ' ';

};

fn();


kembali 0;

}

Outputnya adalah:

5, 2.3, B, 0

Perhatikan bahwa = sendiri harus menjadi karakter pertama dalam klausa capture.

Skema Fungsi Panggilan Balik Klasik dengan Ekspresi Lambda

Program berikut menunjukkan bagaimana skema fungsi panggilan balik klasik dapat dilakukan dengan ekspresi lambda:

#termasuk

menggunakan ruang namajam;

arang *keluaran;


mobilcba= [](arangkeluar[])

{

keluaran=keluar;

};



ruang kosongfungsi utama(arangmemasukkan[],ruang kosong (*untuk)(arang[]))

{

(*untuk)(memasukkan);

biaya<<'untuk fungsi utama'<<' ';

}


ruang kosongfn()

{

biaya<<'Sekarang'<<' ';

}


ke dalamutama()

{

arangmemasukkan[] = 'untuk fungsi panggilan balik';

fungsi utama(masukan, cba);

fn();

biaya<<keluaran<<' ';



kembali 0;

}

Outputnya adalah:

untuk fungsi utama

Sekarang

untuk fungsi panggilan balik

Ingatlah bahwa ketika definisi ekspresi lambda ditetapkan ke variabel dalam lingkup global, badan fungsinya dapat melihat variabel global tanpa menggunakan klausa tangkap.

Tipe trailing-return

Tipe pengembalian ekspresi lambda adalah otomatis, artinya kompiler menentukan tipe pengembalian dari ekspresi pengembalian (jika ada). Jika programmer benar-benar ingin menunjukkan tipe pengembalian, maka dia akan melakukannya seperti pada program berikut:

#termasuk

menggunakan ruang namajam;

mobilfn= [](ke dalamberhenti) -> ke dalam

{

ke dalammenjawab=berhenti+ 3;

kembalimenjawab;

};


ke dalamutama()

{

mobilvariasi=fn(2);

biaya <<variasi<< ' ';


kembali 0;

}

Outputnya adalah 5. Setelah daftar parameter, operator panah diketik. Ini diikuti oleh tipe pengembalian (int dalam kasus ini).

Penutupan

Perhatikan segmen kode berikut:

strukturKla

{

ke dalamIndo= 5;

arangch= 'ke';

}obj1, obj2;

Di sini, Cla adalah nama kelas struct. Obj1 dan obj2 adalah dua objek yang akan diinstansiasi dari kelas struct. Ekspresi Lambda serupa dalam implementasi. Definisi fungsi lambda adalah semacam kelas. Ketika fungsi lambda dipanggil (dipanggil), sebuah objek dibuat dari definisinya. Objek ini disebut penutupan. Ini adalah penutupan yang melakukan pekerjaan yang diharapkan dilakukan lambda.

Namun, pengkodean ekspresi lambda seperti struct di atas akan memiliki obj1 dan obj2 diganti dengan argumen parameter yang sesuai. Program berikut menggambarkan hal ini:

#termasuk

menggunakan ruang namajam;

mobilfn= [](ke dalamparam1,ke dalamparam2)

{

ke dalammenjawab=param1+param2;

kembalimenjawab;

} (2,3);


ke dalamutama()

{

mobildi mana=fn;

biaya <<di mana<< ' ';


kembali 0;

}

Outputnya adalah 5. Argumennya adalah 2 dan 3 dalam tanda kurung. Perhatikan bahwa panggilan fungsi ekspresi lambda, fn, tidak mengambil argumen apa pun karena argumen telah dikodekan di akhir definisi fungsi lambda.

Kesimpulan

Ekspresi lambda adalah fungsi anonim. Itu ada dalam dua bagian: kelas dan objek. Definisinya adalah semacam kelas. Ketika ekspresi dipanggil, sebuah objek terbentuk dari definisi. Objek ini disebut penutupan. Ini adalah penutupan yang melakukan pekerjaan yang diharapkan dilakukan lambda.

Agar ekspresi lambda menerima variabel dari lingkup fungsi luar, diperlukan klausa tangkap yang tidak kosong ke dalam badan fungsinya.