Saya baru mengenal C ++ 11. Saya menulis fungsi lambda rekursif berikut, tetapi tidak mengkompilasi.
sum.cpp
#include <iostream>
#include <functional>
auto term = [](int a)->int {
return a*a;
};
auto next = [](int a)->int {
return ++a;
};
auto sum = [term,next,&sum](int a, int b)mutable ->int {
if(a>b)
return 0;
else
return term(a) + sum(next(a),b);
};
int main(){
std::cout<<sum(1,10)<<std::endl;
return 0;
}
kesalahan kompilasi:
vimal @ linux-718q: ~ / Study / 09C ++ / c ++ 0x / lambda> g ++ -std = c ++ 0x sum.cpp
sum.cpp: Dalam fungsi lambda: sum.cpp: 18: 36: error: ' ((<lambda(int, int)>*)this)-><lambda(int, int)>::sum
' tidak dapat digunakan sebagai fungsi
versi gcc
gcc versi 4.5.0 20091231 (percobaan) (GCC)
Tetapi jika saya mengubah deklarasi sum()
seperti di bawah ini, itu berfungsi:
std::function<int(int,int)> sum = [term,next,&sum](int a, int b)->int {
if(a>b)
return 0;
else
return term(a) + sum(next(a),b);
};
Bisakah seseorang menjelaskan hal ini?
mutable
kata kunci di sana?std::function<int(int,int)> sum = [&](int a, int b) {
Jawaban:
Pikirkan tentang perbedaan antara versi otomatis dan versi tipe yang ditentukan sepenuhnya. Kata kunci otomatis menyimpulkan jenisnya dari apa pun yang diinisialisasi, tetapi apa yang Anda inisialisasi perlu tahu apa jenisnya (dalam hal ini, penutupan lambda perlu mengetahui jenis yang ditangkapnya). Sesuatu dari masalah ayam dan telur.
Di sisi lain, tipe objek fungsi yang ditentukan sepenuhnya tidak perlu "tahu" apa pun tentang apa yang ditugaskan padanya, sehingga penutupan lambda juga dapat sepenuhnya diinformasikan tentang jenis yang ditangkapnya.
Pertimbangkan sedikit modifikasi kode ini dan mungkin lebih masuk akal:
Jelas, ini tidak akan berfungsi dengan otomatis . Fungsi lambda rekursif bekerja dengan sangat baik (setidaknya mereka lakukan di MSVC, di mana saya memiliki pengalaman dengan mereka), hanya saja mereka tidak benar-benar kompatibel dengan inferensi tipe.
sumber
auto
variabel di initializer itu. jenis variabel otomatis belum diketahui saat inisialisasi sedang diproses.sum
selainstd::function<int(int, int)>
, atau apakah spesifikasi C ++ tidak terganggu untuk menyimpulkannya?Caranya adalah memberi makan dalam implementasi lambda untuk dirinya sendiri sebagai parameter , bukan dengan menangkap.
Semua masalah dalam ilmu komputer dapat diselesaikan dengan tingkat tipuan lainnya . Saya pertama kali menemukan trik mudah ini di http://pedromelendez.com/blog/2015/07/16/recursive-lambdas-in-c14/
Ini tidak memerlukan C ++ 14 sementara pertanyaannya adalah pada C ++ 11, tapi mungkin menarik untuk sebagian besar.
Pergi melalui
std::function
juga dimungkinkan tetapi dapat menghasilkan kode lebih lambat. Tapi tidak selalu. Lihatlah jawaban untuk std :: function vs templateIni bukan hanya kekhasan tentang C ++, itu langsung memetakan ke matematika kalkulus lambda. Dari Wikipedia :
sumber
function<>
. Saya tidak mengerti mengapa ada orang yang lebih menyukainya. Sunting: Tampaknya lebih cepat.error: use of ‘[...]’ before deduction of ‘auto’
- diperlukan untuk secara eksplisit menentukan jenis pengembalian (di sisi lain, tidak perlu bisa diubah-ubah).Dengan C ++ 14, sekarang cukup mudah untuk membuat lambda rekursif yang efisien tanpa harus mengeluarkan biaya tambahan
std::function
, hanya dalam beberapa baris kode (dengan suntingan kecil dari aslinya untuk mencegah pengguna mengambil salinan yang tidak disengaja). ):yang dengannya
sum
upaya awal Anda menjadi:Di C ++ 17, dengan CTAD, kita dapat menambahkan panduan deduksi:
Yang meniadakan kebutuhan akan fungsi pembantu. Kita bisa
y_combinator{[](auto self, ...){...}}
langsung menulis .Dalam C ++ 20, dengan CTAD untuk agregat, panduan deduksi tidak diperlukan.
sumber
std::forward<decltype(sum)>(sum)
daripadasum
pada baris terakhir.operator()
sehingga tidak ada untungnya dengan meneruskansum
const
overload jika fungsi-objek yang disediakan memiliki nonconst
-panggilan-operator. Dan gunakan SFINAE dan dihitungnoexcept
untuk keduanya. Juga, tidak perlu lagi fungsi pembuat di C ++ 17.auto sum
salin ... tapi salin areference_wrapper
, yang sama dengan mengambil referensi. Melakukannya sekali dalam implementasi berarti tidak ada penggunaan yang akan secara tidak sengaja menyalin.Saya punya solusi lain, tetapi hanya bekerja dengan lambdas tanpa kewarganegaraan:
Trik di sini adalah lambda dapat mengakses variabel statis dan Anda dapat mengonversi yang stateless ke penunjuk fungsi.
Anda dapat menggunakannya dengan lambda standar:
Kerjanya di GCC 4.7
sumber
Anda dapat membuat panggilan fungsi lambda itu sendiri secara rekursif. Satu-satunya hal yang perlu Anda lakukan adalah merujuknya melalui pembungkus fungsi sehingga kompiler tahu itu kembali dan tipe argumen (Anda tidak dapat menangkap variabel - lambda itu sendiri - yang belum didefinisikan) .
Berhati-hatilah untuk tidak kehabisan ruang pembungkus f.
sumber
Untuk membuat lambda rekursif tanpa menggunakan kelas dan fungsi eksternal (seperti
std::function
atau kombinator titik tetap) kita dapat menggunakan konstruksi berikut di C ++ 14 ( contoh langsung ):cetakan:
Catatan, jenis hasil lambda harus ditentukan secara eksplisit.
sumber
Saya menjalankan benchmark membandingkan fungsi rekursif vs fungsi lambda rekursif menggunakan
std::function<>
metode penangkapan. Dengan optimalisasi penuh diaktifkan pada dentang versi 4.1, versi lambda berjalan lebih lambat.Menghasilkan hasil:
(Catatan: Saya juga mengonfirmasi dengan versi yang mengambil input dari cin, sehingga menghilangkan evaluasi waktu kompilasi)
Dentang juga menghasilkan peringatan kompiler:
Yang diharapkan, dan aman, tetapi harus diperhatikan.
Sangat menyenangkan memiliki solusi di sabuk alat kami, tetapi saya pikir bahasanya akan membutuhkan cara yang lebih baik untuk menangani kasus ini jika kinerja harus sebanding dengan metode saat ini.
catatan:
Seperti yang ditunjukkan oleh komentator, sepertinya versi terbaru VC ++ telah menemukan cara untuk mengoptimalkan ini ke titik kinerja yang sama. Mungkin kita tidak membutuhkan cara yang lebih baik untuk menangani ini (kecuali untuk gula sintaksis).
Juga, seperti beberapa pos SO lainnya telah diuraikan dalam beberapa minggu terakhir, kinerja
std::function<>
itu sendiri mungkin menjadi penyebab fungsi pelambatan vs panggilan secara langsung, setidaknya ketika tangkapan lambda terlalu besar untuk masuk ke dalam beberapastd::function
penggunaan ruang perpustakaan yang dioptimalkan untuk fungsi-fungsi kecil (Saya kira agak seperti berbagai optimasi string pendek?).sumber
Ini adalah implementasi yang sedikit lebih sederhana dari operator fixpoint yang membuatnya sedikit lebih jelas apa yang sebenarnya terjadi.
sumber
std::function
dengan fungsi pointer (core hanya akan bekerja dengan fungsi normal, dan lambdas stateless). Btwfib_nonr
harus menerimafixpoint<int,int>
, jika Andastd::function
perlu meminta salinan baru dari crating*this
.Ini adalah versi yang lebih baik dari solusi Y-combinator berdasarkan pada yang diusulkan oleh @Barry.
Untuk menggunakan ini, seseorang dapat melakukan hal berikut
Ini mirip dengan
let rec
kata kunci di OCaml, meskipun tidak sama.sumber
C ++ 14: Ini adalah stateless anonim rekursif / tanpa kumpulan generik lambdas yang menampilkan semua angka dari 1, 20
Jika saya mengerti benar ini menggunakan solusi Y-combinator
Dan ini adalah versi jumlah (n, m)
sumber
Inilah jawaban terakhir untuk OP. Bagaimanapun, Visual Studio 2010 tidak mendukung menangkap variabel global. Dan Anda tidak perlu menangkapnya karena variabel global dapat diakses secara global. Jawaban berikut menggunakan variabel lokal sebagai gantinya.
sumber
Anda mencoba menangkap variabel (jumlah) yang sedang Anda tentukan. Itu tidak baik.
Saya tidak berpikir benar-benar self-recursive C ++ 0x lambdas adalah mungkin. Anda harus bisa menangkap lambda lain.
sumber
Jawaban ini lebih rendah daripada jawaban Yankes, tetapi tetap saja, inilah jawabannya:
sumber
reinterpret_cast
. Mungkin cara terbaik dalam kasus Anda adalah membuat beberapa struct yang menggantikandp_type
. Itu harus memiliki lapanganfp_type
, dapat dibangun darifp_type
dan memiliki operator()
dengan argumen sepertifp_type
. Ini akan dekat denganstd::function
tetapi akan memungkinkan argumen referensi diri.struct
juga akan menambah tingkat tipuan tambahan. Contohnya bekerja dan para pemainnya memenuhi standar, saya tidak tahu untuk apa-1
itu.-1
saya tidak tahu siapa yang memberikannya kepada Anda, tapi saya pikir itu karenareinterpret_cast
harus digunakan sebagai pilihan terakhir.cast
diduga dijamin untuk bekerja dengan c ++ 11 standar. Menggunakanstruct
, di mata saya, bisa mengalahkan penggunaan objek lambda. Bagaimanapun,struct
Anda mengusulkan adalah functor, memanfaatkan objek lambda.std::function
dan Anda akan memiliki sesuatu yang dekat dengan yang ada dalam pikiran saya. Ini mungkin akan memiliki kinerja yang mirip dengan solusi Anda.Anda memerlukan kombinator titik tetap. Lihat ini .
atau lihat kode berikut:
sumber