C++

Ekspresi Lambda dalam C++

Ekspresi Lambda dalam C++

Mengapa Ekspresi Lambda?

Perhatikan pernyataan berikut:

    int myInt = 52;

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

#termasuk
menggunakan namespace std;
int fn(int par)

int jawaban = par + 3;
jawaban kembali;

int utama()

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 persyaratan untuk memahami artikel ini.

Isi Artikel

  • Ilustrasi Ekspresi Lambda
  • Bagian dari Ekspresi Lambda
  • Tangkapan
  • Skema Fungsi Panggilan Balik Klasik dengan Ekspresi Lambda
  • Tipe trailing-return
  • Penutupan
  • Kesimpulan

Ilustrasi Ekspresi Lambda

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

#termasuk
menggunakan namespace std;
fn otomatis = [](param int)

int jawaban = param + 3;
jawaban kembali;
;
int utama()

variabel otomatis = fn(2);
cout << variab << '\n';
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:

    variabel otomatis = fn(2);

Ini berarti, fn di luar main(), berakhir sebagai pengidentifikasi untuk suatu fungsi. Parameter implisitnya adalah 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 namespace std;
void otherfn (int no1, int (*ptr)(int))

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

int utama()

otherfn(4, [](int param)

int jawaban = param + 3;
jawaban kembali;
);
kembali 0;

Outputnya adalah:

    4  5

Ada dua fungsi di sini, ekspresi lambda dan fungsi otherfn(). Ekspresi lambda adalah argumen kedua dari otherfn(), 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,

    int no2 = (*ptr)(2);

Dalam definisi otherfn() , ia memanggil fungsi lambda dengan argumen 2. Nilai kembalian 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 dapat 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 panggilan 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; variabel yang ditugaskan-fungsi 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) sekitarnya ke dalam badan fungsi ekspresi lambda. Badan fungsi ekspresi lambda dikatakan menangkap variabel ketika 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 namespace std;
int utama()

int nomor = 5;
fn otomatis = [id]()

cout << id << '\n';
;
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, e.g., id di atas, dari ruang lingkup 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 penangkapan didahului oleh ampersand (&) untuk mencapai ini. Program berikut menggambarkan hal ini:

#termasuk
menggunakan namespace std;
int utama()

int nomor = 5; kaki pelampung = 2.3; char ch = 'A';
fn otomatis = [&id, &ft, &ch]()

nomor = 6; kaki = 3.4; ch = 'B';
;
fn();
cout << id << ", " <<  ft << ", " <<  ch << '\n';
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 seperti sekarang. Untuk mencapai penangkapan berdasarkan nilai, setiap variabel yang diulang dalam klausa penangkapan tidak didahului oleh apa pun. Program berikut menggambarkan hal ini:

#termasuk
menggunakan namespace std;
int utama()

int nomor = 5; kaki pelampung = 2.3; char ch = 'A';
fn otomatis = [id, kaki, ch]()

//id = 6; kaki = 3.4; ch = 'B';
cout << id << ", " <<  ft << ", " <<  ch << '\n';
;
fn();
nomor = 6; kaki = 3.4; ch = 'B';
cout << id << ", " <<  ft << ", " <<  ch << '\n';
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 namespace std;
int utama()

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

ch = 'B'; bl = salah;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
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 capture. Program berikut menggambarkan hal ini:

#termasuk
menggunakan namespace std;
int utama()

int nomor = 5; kaki mengambang = 2.3; char ch = 'A'; bool bl = benar;
fn otomatis = [&]()

nomor = 6; kaki = 3.4; ch = 'B'; bl = salah;
;
fn();
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
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 namespace std;
int utama()

int nomor = 5; kaki mengambang = 2.3; char ch = 'A'; bool bl = benar;
fn otomatis = [&, id, kaki]()

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

Outputnya adalah:

5, 2.3, B, 0

Perhatikan bahwa & sendirian (i.e., & 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 capture. Program berikut menggambarkan hal ini:

#termasuk
menggunakan namespace std;
int utama()

int nomor = 5; kaki pelampung = 2.3; char ch = 'A'; bool bl = benar;
fn otomatis = [=]()

cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
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 namespace std;
int utama()

int nomor = 5; kaki pelampung = 2.3; char ch = 'A'; bool bl = benar;
fn otomatis = [=, &ch, &bl]()

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

Outputnya adalah:

5, 2.3, B, 0

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

Skema Fungsi Panggilan Balik Klasik dengan Ekspresi Lambda

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

#termasuk
menggunakan namespace std;
karakter *keluaran;
cba otomatis = [](menghabiskan[])

keluaran = keluar;
;
void principalFunc(char input[], void (*pt)(char[]))

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

batal fn()

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

int utama()

char input[] = "untuk fungsi panggilan balik";
principalFunc(masukan, cba);
fn();
cout<kembali 0;

Outputnya adalah:

untuk fungsi utama
Sekarang
untuk fungsi panggilan balik

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

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 namespace std;
fn otomatis = [](param int) -> int

int jawaban = param + 3;
jawaban kembali;
;
int utama()

variabel otomatis = fn(2);
cout << variab << '\n';
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:

struct Cla

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

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

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

#termasuk
menggunakan namespace std;
fn otomatis = [](int param1, int param2)

int jawaban = param1 + param2;
jawaban kembali;
(2, 3);
int utama()

var otomatis = fn;
cout << var << '\n';
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. Penutupanlah yang melakukan pekerjaan yang diharapkan dilakukan lambda.

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

Kursor melompat atau bergerak secara acak saat mengetik di Windows 10
Jika Anda menemukan bahwa kursor mouse Anda melompat atau bergerak sendiri, secara otomatis, acak saat mengetik di laptop atau komputer Windows, maka ...
Cara membalikkan arah gulir Mouse dan Touchpads di Windows 10
Mouse dan Panel sentuhs tidak hanya membuat komputasi menjadi mudah tetapi juga lebih efisien dan tidak memakan banyak waktu. Kami tidak dapat membaya...
Cara mengubah penunjuk Mouse dan ukuran kursor, warna & skema pada Windows 10
Penunjuk mouse dan kursor di Windows 10 adalah aspek yang sangat penting dari sistem operasi. Ini dapat dikatakan untuk sistem operasi lain juga, jadi...