C ++

Ekspresi Lambda dalam C ++

Ekspresi Lambda dalam C ++

Mengapa Ekspresi Lambda?

Pertimbangkan pernyataan berikut:

    int myInt = 52;

Di sini, myInt adalah pengecam, nilai. 52 adalah harfiah, nilai. Hari ini, adalah mungkin untuk membuat kod fungsi secara khusus dan meletakkannya pada kedudukan 52. Fungsi sedemikian disebut ungkapan lambda. Pertimbangkan juga program pendek berikut:

#sertakan
menggunakan ruang nama std;
int fn (setaraf int)

int jawapan = par + 3;
jawapan balik;

int utama ()

fn (5);
pulangan 0;

Hari ini, adalah mungkin untuk membuat kod fungsi secara khusus dan meletakkannya pada kedudukan argumen 5, panggilan fungsi, fn (5). Fungsi sedemikian disebut ungkapan lambda. Ungkapan lambda (fungsi) dalam kedudukan itu adalah nilai.

Sebarang literal kecuali literal string adalah prvalue. Ekspresi lambda adalah reka bentuk fungsi khas yang sesuai dengan literal dalam kod. Ini adalah fungsi tanpa nama (tanpa nama). Artikel ini menerangkan ungkapan utama C ++ baru, yang disebut ungkapan lambda. Pengetahuan asas dalam C ++ adalah syarat untuk memahami artikel ini.

Kandungan Artikel

  • Ilustrasi Lambda Expression
  • Bahagian Ekspresi Lambda
  • Tangkapan
  • Skema Fungsi Panggilan Balik Klasik dengan Lambda Expression
  • Jenis-trailing-return
  • Penutupan
  • Kesimpulannya

Ilustrasi Lambda Expression

Dalam program berikut, fungsi, yang merupakan ekspresi lambda, diberikan kepada pemboleh ubah:

#sertakan
menggunakan ruang nama std;
auto fn = [] (int param)

int jawapan = param + 3;
jawapan balik;
;
int utama ()

pemboleh ubah automatik = fn (2);
cout << variab << '\n';
pulangan 0;

Keluarannya adalah:

    5

Di luar fungsi utama (), terdapat pemboleh ubah, fn. Jenisnya adalah automatik. Secara automatik dalam keadaan ini bermaksud bahawa jenis sebenar, seperti int atau float, ditentukan oleh operan kanan pengendali tugasan (=). Di sebelah kanan pengendali tugasan terdapat ungkapan lambda. Ungkapan lambda adalah fungsi tanpa jenis pengembalian sebelumnya. Perhatikan penggunaan dan kedudukan tanda kurung persegi, []. Fungsi mengembalikan 5, int, yang akan menentukan jenis untuk fn.

Dalam fungsi utama (), terdapat pernyataan:

    pemboleh ubah automatik = fn (2);

Ini bermaksud, fn di luar main (), berakhir sebagai pengecam fungsi. Parameter tersiratnya adalah dari ungkapan lambda. Jenis pemboleh ubah adalah automatik.

Perhatikan bahawa ungkapan lambda diakhiri dengan titik koma, sama seperti definisi kelas atau struktur, diakhiri dengan titik koma.

Dalam program berikut, fungsi, yang merupakan ungkapan lambda yang mengembalikan nilai 5, adalah argumen untuk fungsi lain:

#sertakan
menggunakan ruang nama std;
batal otherfn (int no1, int (* ptr) (int))

int no2 = (* ptr) (2);
cout << no1 << " << no2 << '\n';

int utama ()

otherfn (4, [] (int param)

int jawapan = param + 3;
jawapan balik;
);
pulangan 0;

Keluarannya adalah:

    4 5

Terdapat dua fungsi di sini, ungkapan lambda dan fungsi lain (). Ungkapan lambda adalah argumen kedua dari lainfn (), disebut dalam utama (). Perhatikan bahawa fungsi lambda (ungkapan) tidak berakhir dengan titik koma dalam panggilan ini kerana, di sini, ia adalah argumen (bukan fungsi yang berdiri sendiri).

Parameter fungsi lambda dalam definisi fungsi otherfn () adalah penunjuk kepada fungsi. Penunjuk mempunyai nama, ptr. Nama, ptr, digunakan dalam definisi otherfn () untuk memanggil fungsi lambda.

Penyataan,

    int no2 = (* ptr) (2);

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

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

Bahagian Ekspresi Lambda

Bahagian fungsi lambda khas adalah seperti berikut:

    [] ()
  • [] adalah klausa tangkapan. Ia boleh mempunyai barang.
  • () adalah untuk senarai parameter.
  • adalah untuk badan fungsi. Sekiranya fungsinya berdiri sendiri, maka ia harus diakhiri dengan titik koma.

Tangkapan

Definisi fungsi lambda boleh diberikan kepada pemboleh ubah atau digunakan sebagai argumen untuk panggilan fungsi yang berbeza. Definisi panggilan fungsi seperti itu harus memiliki parameter, penunjuk ke fungsi, sesuai dengan definisi fungsi lambda.

Definisi fungsi lambda berbeza dengan definisi fungsi normal. Ia boleh diberikan kepada pemboleh ubah dalam skop global; fungsi ini ditugaskan ke pemboleh ubah juga dapat dikodkan di dalam fungsi lain. Apabila ditugaskan ke pemboleh ubah skop global, badannya dapat melihat pemboleh ubah lain dalam skop global. Apabila ditugaskan pada pemboleh ubah dalam definisi fungsi normal, badannya dapat melihat pemboleh ubah lain dalam ruang lingkup fungsi hanya dengan bantuan klausa penangkapan, [].

Klausa penangkapan [], juga dikenal sebagai pengantar lambda, membolehkan pemboleh ubah dihantar dari ruang lingkup (fungsi) sekitarnya ke dalam badan fungsi ekspresi lambda. Badan fungsi ekspresi lambda dikatakan menangkap pemboleh ubah ketika menerima objek. Tanpa klausa tangkapan [], pemboleh ubah tidak dapat dikirim dari ruang lingkup sekitarnya ke badan fungsi ekspresi lambda. Program berikut menggambarkan ini, dengan skop fungsi () utama, seperti skop sekitarnya:

#sertakan
menggunakan ruang nama std;
int utama ()

int id = 5;
automatik fn = [id] ()

cout << id << '\n';
;
fn ();
pulangan 0;

Keluarannya adalah 5. Tanpa nama, id, di dalam [], ungkapan lambda tidak akan dapat melihat pemboleh ubah id dari fungsi utama ().

Menangkap dengan Rujukan

Contoh penggunaan klausa tangkapan di atas adalah menangkap berdasarkan nilai (lihat perincian di bawah). Dalam menangkap dengan rujukan, lokasi (penyimpanan) pemboleh ubah, e.g., id di atas, dari ruang lingkup sekitarnya, disediakan di dalam badan fungsi lambda. Jadi, mengubah nilai pemboleh ubah di dalam badan fungsi lambda akan mengubah nilai pemboleh ubah yang sama dalam ruang lingkup sekitarnya. Setiap pemboleh ubah yang diulang dalam klausa tangkapan didahului oleh ampersand (&) untuk mencapainya. Program berikut menggambarkan ini:

#sertakan
menggunakan ruang nama std;
int utama ()

int id = 5; apungan ft = 2.3; char ch = 'A';
auto fn = [& id, & ft, & ch] ()

id = 6; kaki = 3.4; ch = 'B';
;
fn ();
cout << id << ", " <<  ft << ", " <<  ch << '\n';
pulangan 0;

Keluarannya adalah:

    6, 3.4, B

Mengesahkan bahawa nama pemboleh ubah di dalam badan fungsi ekspresi lambda adalah untuk pemboleh ubah yang sama di luar ungkapan lambda.

Menangkap mengikut Nilai

Dalam menangkap berdasarkan nilai, salinan lokasi pemboleh ubah, dari lingkup sekitarnya, tersedia di dalam badan fungsi lambda. Walaupun pemboleh ubah di dalam badan fungsi lambda adalah salinan, nilainya tidak dapat diubah di dalam badan seperti sekarang. Untuk mencapai tangkapan berdasarkan nilai, setiap pemboleh ubah yang diulang dalam klausa tangkapan tidak didahului oleh apa-apa. Program berikut menggambarkan ini:

#sertakan
menggunakan ruang nama std;
int utama ()

int id = 5; apungan ft = 2.3; char ch = 'A';
automatik fn = [id, ft, ch] ()

// id = 6; kaki = 3.4; ch = 'B';
cout << id << ", " <<  ft << ", " <<  ch << '\n';
;
fn ();
id = 6; kaki = 3.4; ch = 'B';
cout << id << ", " <<  ft << ", " <<  ch << '\n';
pulangan 0;

Keluarannya adalah:

5, 2.3, A
6, 3.4, B

Sekiranya penunjuk komen dikeluarkan, program tidak akan disusun. Penyusun akan mengeluarkan mesej ralat bahawa pemboleh ubah di dalam definisi badan fungsi ungkapan lambda tidak dapat diubah. Walaupun pemboleh ubah tidak dapat diubah di dalam fungsi lambda, mereka dapat diubah di luar fungsi lambda, seperti yang ditunjukkan oleh output program di atas.

Mencampurkan Tangkapan

Menangkap dengan rujukan dan menangkap dengan nilai dapat dicampur, seperti yang ditunjukkan oleh program berikut:

#sertakan
menggunakan ruang nama std;
int utama ()

int id = 5; apungan ft = 2.3; char ch = 'A'; bool bl = benar;
auto fn = [id, ft, & ch, & bl] ()

ch = 'B'; bl = palsu;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
pulangan 0;

Keluarannya adalah:

    5, 2.3, B, 0

Apabila semua ditangkap, adalah dengan rujukan:

Sekiranya semua pemboleh ubah yang akan ditangkap ditangkap dengan rujukan, maka hanya satu & akan mencukupi dalam klausa tangkapan. Program berikut menggambarkan ini:

#sertakan
menggunakan ruang nama std;
int utama ()

int id = 5; apungan ft = 2.3; char ch = 'A'; bool bl = benar;
automatik fn = [&] ()

id = 6; kaki = 3.4; ch = 'B'; bl = palsu;
;
fn ();
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
pulangan 0;

Keluarannya adalah:

6, 3.4, B, 0

Sekiranya beberapa pemboleh ubah ditangkap dengan rujukan dan yang lain berdasarkan nilai, maka satu & akan mewakili semua rujukan, dan selebihnya tidak akan didahului oleh apa-apa, seperti yang ditunjukkan oleh program berikut:

menggunakan ruang nama std;
int utama ()

int id = 5; apungan ft = 2.3; char ch = 'A'; bool bl = benar;
automatik fn = [&, id, ft] ()

ch = 'B'; bl = palsu;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
pulangan 0;

Keluarannya adalah:

5, 2.3, B, 0

Perhatikan bahawa & bersendirian (i.e., & tidak diikuti oleh pengecam) mesti menjadi watak pertama dalam klausa tangkapan.

Apabila semua ditangkap, berdasarkan nilai:

Sekiranya semua pemboleh ubah yang akan ditangkap ditangkap berdasarkan nilai, maka hanya satu = yang mencukupi dalam klausa tangkapan. Program berikut menggambarkan ini:

#sertakan
menggunakan ruang nama std;
int utama ()

int id = 5; apungan ft = 2.3; char ch = 'A'; bool bl = benar;
automatik fn = [=] ()

cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
pulangan 0;

Keluarannya adalah:

5, 2.3, A, 1

Nota: = hanya boleh dibaca, mulai sekarang.

Sekiranya beberapa pemboleh ubah ditangkap oleh nilai dan yang lain dengan rujukan, maka satu = akan mewakili semua pemboleh ubah yang disalin hanya baca, dan selebihnya masing-masing akan mempunyai &, seperti yang ditunjukkan oleh program berikut:

#sertakan
menggunakan ruang nama std;
int utama ()

int id = 5; apungan ft = 2.3; char ch = 'A'; bool bl = benar;
automatik fn = [=, & ch, & bl] ()

ch = 'B'; bl = palsu;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
pulangan 0;

Keluarannya adalah:

5, 2.3, B, 0

Perhatikan bahawa = sahaja harus menjadi watak pertama dalam klausa tangkapan.

Skema Fungsi Panggilan Balik Klasik dengan Lambda Expression

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

#sertakan
menggunakan ruang nama std;
output char *;
auto cba = [] (habiskan [])

output = keluar;
;
void prinsipalFunc (input char [], void (* pt) (char []))

(* pt) (input);
cout<<"for principal function"<<'\n';

batal fn ()

cout<<"Now"<<'\n';

int utama ()

input char [] = "untuk fungsi panggilan balik";
prinsipalFunc (input, cba);
fn ();
cout<pulangan 0;

Keluarannya adalah:

untuk fungsi pokok
Sekarang
untuk fungsi panggilan balik

Ingatlah bahawa apabila definisi ekspresi lambda diberikan kepada pemboleh ubah dalam skop global, badan fungsinya dapat melihat pemboleh ubah global tanpa menggunakan klausa tangkapan.

Jenis-trailing-return

Jenis pengembalian ungkapan lambda adalah automatik, yang bermaksud penyusun menentukan jenis pengembalian dari ungkapan kembali (jika ada). Sekiranya pengaturcara benar-benar ingin menunjukkan jenis pengembalian, maka dia akan melakukannya seperti dalam program berikut:

#sertakan
menggunakan ruang nama std;
auto fn = [] (int param) -> int

int jawapan = param + 3;
jawapan balik;
;
int utama ()

pemboleh ubah automatik = fn (2);
cout << variab << '\n';
pulangan 0;

Keluarannya adalah 5. Selepas senarai parameter, pengendali anak panah ditaip. Ini diikuti oleh jenis pengembalian (int dalam kes ini).

Penutupan

Pertimbangkan segmen kod berikut:

struktur Cla

int id = 5;
char ch = 'a';
obj1, obj2;

Di sini, Cla adalah nama kelas struct.  Obj1 dan obj2 adalah dua objek yang akan dibuat dari kelas struct. Ekspresi lambda serupa dalam pelaksanaannya. Definisi fungsi lambda adalah sejenis kelas. Apabila fungsi lambda disebut (dipanggil), suatu objek dibentuk dari definisinya. Objek ini dipanggil penutupan. Penutupan inilah yang melakukan kerja yang diharapkan oleh lambda.

Walau bagaimanapun, pengekodan ungkapan lambda seperti struktur di atas akan mempunyai obj1 dan obj2 digantikan oleh argumen parameter yang sesuai. Program berikut menggambarkan ini:

#sertakan
menggunakan ruang nama std;
auto fn = [] (int param1, int param2)

int jawapan = param1 + param2;
jawapan balik;
(2, 3);
int utama ()

auto var = fn;
cout << var << '\n';
pulangan 0;

Keluarannya adalah 5. Argumen adalah 2 dan 3 dalam kurungan. Perhatikan bahawa panggilan fungsi ekspresi lambda, fn, tidak mengambil argumen kerana argumen telah dikodkan pada akhir definisi fungsi lambda.

Kesimpulannya

Ungkapan lambda adalah fungsi tanpa nama. Ia ada dalam dua bahagian: kelas dan objek. Takrifnya adalah jenis kelas. Apabila ungkapan disebut, objek terbentuk dari definisi. Objek ini dipanggil penutupan. Penutupan inilah yang melakukan kerja yang diharapkan oleh lambda.

Agar ungkapan lambda menerima pemboleh ubah dari skop fungsi luar, ia memerlukan klausa penangkapan yang tidak kosong ke badan fungsinya.

Cara Menunjukkan Kaunter FPS dalam Permainan Linux
Permainan Linux mendapat dorongan utama ketika Valve mengumumkan sokongan Linux untuk klien Steam dan permainan mereka pada tahun 2012. Sejak itu, ban...
Cara memuat turun dan Mainkan Sid Meier's Civilization VI di Linux
Pengenalan permainan Civilization 6 adalah konsep moden mengenai konsep klasik yang diperkenalkan dalam siri permainan Age of Empires. Idea itu cukup ...
Cara Memasang dan Memainkan Doom di Linux
Pengenalan Doom Seri Doom berasal dari tahun 90an selepas pembebasan Doom yang asal. Ini adalah hit seketika dan sejak saat itu dan seterusnya siri pe...