C ++ 11'de lambda ifadesi nedir?


1487

C ++ 11'de lambda ifadesi nedir? Ne zaman kullanmalıyım? Girişlerinden önce mümkün olmayan ne tür bir sorunu çözüyorlar?

Birkaç örnek ve kullanım örnekleri yararlı olacaktır.


14
Lambda'nın çok faydalı olduğu bir durum gördüm: Bir meslektaşım, bir alan optimizasyonu problemini çözmek için milyonlarca yinelemeye sahip kod yapıyordu. Lamda kullanıldığında algoritma uygun bir fonksiyondan çok daha hızlıydı! Derleyici Visual C ++ 2013.
sergiol

Yanıtlar:


1490

Sorun

C ++ çok kullanışlı olabilecek std::for_eachve gibi kullanışlı genel işlevler içerir std::transform. Ne yazık ki onlar da oldukça, özellikle kullanımına hantal olabilir funktor Uygulamak istediğiniz belirli işleve benzersizdir.

#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);
}

Sadece bir fkez ve belirli bir yerde kullanırsanız, sadece önemsiz ve bir kerelik bir şey yapmak için tüm bir sınıfı yazmak aşırıya kaçmış gibi görünüyor.

C ++ 03'te, işlevi yerel tutmak için aşağıdakine benzer bir şey yazmaya cazip olabilirsiniz:

void func2(std::vector<int>& v) {
  struct {
    void operator()(int) {
       // do something
    }
  } f;
  std::for_each(v.begin(), v.end(), f);
}

ancak buna izin verilmez, C ++ 03'te şablon işlevine fgeçirilemez .

Yeni çözüm

C ++ 11 lambdas tanıtmak yerine bir satır içi, anonim functor yazmak için izin verir struct f. Küçük basit örnekler için bu okunması daha temiz olabilir (her şeyi tek bir yerde tutar) ve örneğin en basit biçimde bakımı potansiyel olarak daha basit olabilir:

void func3(std::vector<int>& v) {
  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

Lambda fonksiyonları anonim işlevler için sadece sözdizimsel şekerdir.

İade türleri

Basit durumlarda lambda'nın dönüş tipi sizin için çıkarılır, örneğin:

void func4(std::vector<double>& v) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [](double d) { return d < 0.00001 ? 0 : d; }
                 );
}

ancak daha karmaşık lambdalar yazmaya başladığınızda, dönüş türünün derleyici tarafından çıkarılamadığı durumlarda hızlı bir şekilde karşılaşacaksınız, örneğin:

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;
            }
        });
}

Bunu çözmek için aşağıdakileri kullanarak bir lambda işlevi için açıkça bir dönüş türü belirtmenize izin verilir -> 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;
            }
        });
}

"Yakalama" değişkenleri

Şimdiye kadar içindeki lambda'ya aktarılandan başka bir şey kullanmadık, ancak lambda içindeki diğer değişkenleri de kullanabiliriz. Diğer değişkenlere erişmek istiyorsanız, []bu örneklerde şu ana kadar kullanılmamış olan yakalama maddesini ( ifadenin) kullanabilirsiniz, örneğin:

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;
            }
        });
}

Sırasıyla &ve kullanarak belirtebileceğiniz referans ve değere göre yakalayabilirsiniz =:

  • [&epsilon] referans ile yakalama
  • [&] lambdada kullanılan tüm değişkenleri referans alarak yakalar
  • [=] lambdada kullanılan tüm değişkenleri değere göre yakalar
  • [&, epsilon] [&], ancak epsilon değerleriyle değişkenleri yakalar
  • [=, &epsilon] [=] gibi değişkenleri yakalar, ancak referans olarak epsilon

Oluşturulan operator()olduğunu constyakalar olacağını ima, varsayılan olarak constvarsayılan olarak eriştiğinizde. Bu, aynı girişe sahip her çağrının aynı sonucu üretmesi gibi bir etkiye sahiptir, ancak lambda'yı bununmutableoperator() üretilmesini istemeyecek şekilde işaretleyebilirsinizconst .


9
@Yakk tuzağa düştün. yakalama olmayan lambdaların işlev türü işaretleyicilerine örtük bir dönüşümü vardır. dönüştürme işlevi her constzaman ...
Johannes Schaub - litb

2
@ JohannesSchaub-litb oh sinsi - ve çağırdığınızda gerçekleşir ()- sıfır bağımsız değişken lambda olarak geçirilir, ancak () constlambda ile eşleşmediğinden, izin verilen bir döküm içeren izin verilen bir tür dönüşüm arar -to-işlev-pointer ve sonra bunu çağırır! Sinsi!
Yakk - Adam Nevraumont

2
İlginç - Başlangıçta lambdaların functorlardan ziyade anonim işlevler olduğunu ve yakalamaların nasıl çalıştığı konusunda kafası karışmıştı.
user253751

49
Lambdaları programınızda değişken olarak kullanmak istiyorsanız, şunları kullanabilirsiniz: std::function<double(int, bool)> f = [](int a, bool b) -> double { ... }; Ancak genellikle, derleyicinin türü çıkarmasına izin veririz: auto f = [](int a, bool b) -> double { ... }; (ve unutmayın #include <functional>)
Evert Heylen

11
Sanırım herkes return d < 0.00001 ? 0 : d;, işlenenlerden biri bir tamsayı sabiti (neden 2. ve 3. işlenenlerin olağan aritmetik yoluyla birbirine dengelendiği)? hangisi seçilirse seçilsin). Değiştirmek 0.0 : d, örneğin anlaşılmasını kolaylaştırır.
Lundin

830

Lambda fonksiyonu nedir?

Lambda fonksiyonunun C ++ konsepti lambda hesabı ve fonksiyonel programlamadan kaynaklanır. Lambda, yeniden kullanımı imkansız olan ve adlandırılmaya değer olmayan kısa kod snippet'leri için (gerçek programlamada, teoride değil) yararlı olan isimsiz bir işlevdir.

C ++ 'da lambda işlevi şöyle tanımlanır

[]() { } // barebone lambda

ya da tüm ihtişamıyla

[]() mutable -> T { } // T is the return type, still lacking throw()

[]yakalama listesi, ()bağımsız değişken listesi ve {}işlev gövdesidir.

Yakalama listesi

Yakalama listesi, lambda'nın dışından fonksiyon gövdesi içinde neyin mevcut olması gerektiğini ve nasıl olduğunu tanımlar. Şunlardan biri olabilir:

  1. bir değer: [x]
  2. bir referans [& x]
  3. şu anda referans kapsamında olan herhangi bir değişken [&]
  4. 3 ile aynı, ancak [=] değerine göre

Yukarıdakilerden herhangi birini virgülle ayrılmış bir listede karıştırabilirsiniz [x, &y].

Tartışma listesi

Bağımsız değişken listesi, diğer C ++ işlevleriyle aynıdır.

İşlev gövdesi

Lambda gerçekten çağrıldığında çalıştırılacak kod.

Dönüş tipi kesinti

Bir lambda yalnızca bir dönüş ifadesine sahipse, dönüş türü atlanabilir ve örtük türüne sahiptir decltype(return_statement).

değişken

Bir lambda değiştirilebilir (örneğin []() mutable { }) olarak işaretlenmişse , değere göre yakalanan değerleri değiştirmesine izin verilir.

Kullanım örnekleri

ISO standardı tarafından tanımlanan kütüphane lambdalardan büyük ölçüde yararlanır ve kullanıcıların bazı erişilebilirlik kapsamındaki kodlarını küçük functorlarla karıştırmak zorunda olmadığı için kullanılabilirliği birkaç çubuk artırır.

C ++ 14

C ++ 'ta 14 lambda çeşitli tekliflerle genişletilmiştir.

Başlatılan Lambda Yakalamaları

Yakalama listesinin bir öğesi ile şimdi başlatılabilir =. Bu, değişkenlerin yeniden adlandırılmasına ve hareket ederek yakalanmasına izin verir. Standarttan alınan bir örnek:

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.

ve aşağıdakilerden nasıl çekim yapılacağını gösteren Wikipedia'dan alınmıştır std::move:

auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};

Jenerik Lambdas

Lambdas artık genel olabilir ( eğer çevredeki kapsamın herhangi bir yerinde bir tür şablon argümanı olsaydı buraya autoeşdeğer olurdu ):TT

auto lambda = [](auto x, auto y) {return x + y;};

Geliştirilmiş Dönüş Tipi Kesintisi

C ++ 14, her işlev için türetilmiş döndürme türlerine izin verir ve bunu formun işlevleriyle sınırlamaz return expression;. Bu da lambdalara kadar uzanır.


2
Yukarıda başlatılan lambda yakalamaları örneğinizde, lamba işlevini neden (); Bu [] () {} () gibi görünür; onun yerine [](){};. Ayrıca x'in değeri 5 olmamalı mı?
Ramakrishnan Kannan

6
@RamakrishnanKannan: 1) () lambda'yı tanımladıktan hemen sonra aramak ve geri dönüş değerini vermek için oradalar. Y değişkeni lambda değil, bir tamsayıdır. 2) Hayır, x = 5 lambda için yereldir (dış kapsam değişkeni x ile aynı ada sahip olan değere göre bir yakalama) ve sonra x + 2 = 5 + 2 döndürülür. Dış değişken x'in yeniden atanması r referansı yoluyla gerçekleşir r = &x; r += 2;, ancak bu orijinal 4 değerine olur.
Vee

167

Lambda ifadeleri genellikle algoritmaları başka bir işleve geçirilebilmeleri için kapsüllemek için kullanılır. Bununla birlikte, tanımdan hemen sonra bir lambda çalıştırmak mümkündür :

[&](){ ...your code... }(); // immediately executed lambda expression

işlevsel olarak eşittir

{ ...your code... } // simple code block

Bu, lambda ifadelerini karmaşık işlevleri yeniden düzenlemek için güçlü bir araç yapar . Yukarıda gösterildiği gibi bir kod bölümünü lambda fonksiyonuna sararak başlayabilirsiniz. Açık parametreleme işlemi daha sonra her adımdan sonra ara testlerle kademeli olarak gerçekleştirilebilir. Kod bloğunu tam olarak parametrelendirdiğinizde (kaldırılmasıyla gösterildiği gibi &), kodu harici bir konuma taşıyabilir ve normal bir işlev haline getirebilirsiniz.

Benzer şekilde, algoritmanın sonucuna göre değişkenleri başlatmak için lambda ifadelerini kullanabilirsiniz ...

int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!

Gibi program mantığı bölünmesi bir şekilde , hatta yararlı başka lambda ifadesi için bağımsız değişken olarak lambda ifade geçmek bulabilir ...

[&]( std::function<void()> algorithm ) // wrapper section
   {
   ...your wrapper code...
   algorithm();
   ...your wrapper code...
   }
([&]() // algorithm section
   {
   ...your algorithm code...
   });

Lambda ifadeleri , yinelenen mantıktan kaçınmanın uygun bir yolu olabilecek adlandırılmış iç içe işlevler oluşturmanıza da olanak tanır. Adsız lambdaların kullanılması, önemsiz olmayan bir işlevi başka bir işleve parametre olarak geçirirken gözlerde (anonim satır içi lambdalara kıyasla) biraz daha kolay olma eğilimindedir. Not: Kıvırcık ayraç kapatıldıktan sonra noktalı virgül unutmayın.

auto algorithm = [&]( double x, double m, double b ) -> double
   {
   return m*x+b;
   };

int a=algorithm(1,2,3), b=algorithm(4,5,6);

Sonraki profil oluşturma, işlev nesnesi için önemli başlatma yükünü ortaya çıkarırsa, bunu normal bir işlev olarak yeniden yazmayı seçebilirsiniz.


11
Bu sorunun 1.5 yıl önce sorulduğunu ve son etkinliğin neredeyse 1 yıl önce olduğunu fark ettiniz mi? Her neyse, daha önce görmediğim bazı ilginç fikirlere katkıda bulunuyorsun!
Piotr99

7
Eşzamanlı olarak tanımla ve yürüt ipucu için teşekkürler! Onun 'için değer için contidion olarak bu eserler belirterek düşünüyorum if: tabloların if ([i]{ for (char j : i) if (!isspace(j)) return false ; return true ; }()) // i is all whitespace, varsayarak ibir olduğunustd::string
Blacklight Parlayan

74
Yani şu bir ifadedir: [](){}();.
nobar

8
Ihh! Python'un (lambda: None)()sözdizimi çok daha okunaklıdır.
dan04

9
@nobar - haklısın, yanlış yazdım. Bu yasal (Bu sefer test ettim)main() {{{{((([](){{}}())));}}}}
Mark Lakata

38

Yanıtlar

S: C ++ 11'de lambda ifadesi nedir?

C: Kaputun altında, aşırı yük operatörü () const ile otomatik olarak oluşturulmuş bir sınıfın nesnesidir . Bu nesneye kapatma denir ve derleyici tarafından oluşturulur. Bu 'kapatma' konsepti, C ++ 11'in bağlama konseptine yakındır. Ancak lambdalar genellikle daha iyi kod üretir. Kapaklar üzerinden yapılan aramalar da tam satır içi çizgiye izin verir.

S: Ne zaman kullanabilirim?

C: "Basit ve küçük mantığı" tanımlamak ve derleyiciden önceki sorudan nesil gerçekleştirmesini istemek. Derleyiciye işleç () içinde olmasını istediğiniz bazı ifadeler verirsiniz. Diğer tüm şeyler derleyici size üretecektir.

S: Girişten önce mümkün olmayan hangi sınıf problemini çözüyorlar?

C: Özel ekleme, alt temas işlemleri için işlevler yerine aşırı yüklenen operatörler gibi bir tür sözdizimi şekeri ... Ama bazı sınıflara 1-3 satır gerçek mantık sarmak için gereksiz koddan daha fazla satır kaydeder , vb! Bazı mühendisler, eğer satır sayısı daha azsa, hata yapma şansının daha az olduğunu düşünüyorlar (ben de öyle düşünüyorum)

Kullanım örneği

auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);

Lamdalarla ilgili ekstralar, soru ile kapsanmıyor. İlgilenmiyorsanız bu bölümü yoksayın

1. Yakalanan değerler. Yakalamak için neler yapabilirsiniz

1.1. Lambda cinsinden statik depolama süresine sahip bir değişkene başvurabilirsiniz. Hepsi yakalandı.

1.2. Lambda'yı "değere göre" yakalama değerleri için kullanabilirsiniz. Böyle bir durumda yakalanan değişkenler fonksiyon nesnesine kopyalanır (kapanır).

[captureVar1,captureVar2](int arg1){}

1.3. Referans olarak yakalayabilirsiniz. & - bu bağlamda işaretçiler değil referans anlamına gelir.

   [&captureVar1,&captureVar2](int arg1){}

1.4. Statik olmayan tüm değişkenleri değere veya referansa göre yakalamak için gösterim vardır

  [=](int arg1){} // capture all not-static vars by value

  [&](int arg1){} // capture all not-static vars by reference

1.5. Statik olmayan tüm değişkenleri değere veya referansa göre yakalamak ve ikram etmek için gösterim vardır. Daha. Örnekler: Tüm statik olmayan değişkenleri değere göre yakalayın, ancak referans yakalama Param2 ile yakalayın

[=,&Param2](int arg1){} 

Statik olmayan tüm değişkenleri referans olarak, ancak değer yakalama Param2 ile yakalayın

[&,Param2](int arg1){} 

2. Dönüş tipi kesinti

2.1. Lambda bir ifade ise Lambda dönüş tipi çıkarılabilir. Veya açıkça belirtebilirsiniz.

[=](int arg1)->trailing_return_type{return trailing_return_type();}

Lambda'nın birden fazla ifadesi varsa, geri dönüş türü, sondaki dönüş türü aracılığıyla belirtilmelidir. Ayrıca, benzer sözdizimi otomatik işlevlere ve üye işlevlerine uygulanabilir

3. Yakalanan değerler. Ne yakalayamazsın

3.1. Nesnenin üye değişkenini değil, yalnızca yerel değişkenleri yakalayabilirsiniz.

4. Döngüler

4.1 !! Lambda bir işlev işaretçisi değildir ve anonim bir işlev değildir, ancak yakalama gerektirmeyen lambdalar dolaylı olarak bir işlev işaretleyicisine dönüştürülebilir.

ps

  1. Lambda dilbilgisi hakkında daha fazla bilgi Programlama Dili Çalışma taslağı C ++ # 337, 2012-01-16, 5.1.2'de bulunabilir. Lambda İfadeleri, s.88

  2. C ++ 14'te "init capture" olarak adlandırılan ekstra özellik eklenmiştir. Kapanış veri üyelerinin keyfi olarak bildirilmesine izin verir:

    auto toFloat = [](int value) { return float(value);};
    auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};

Bu [&,=Param2](int arg1){}geçerli bir sözdizimi gibi görünmüyor. Doğru form[&,Param2](int arg1){}
GetFree

Teşekkürler. Önce bu parçacığı derlemeye çalıştım. Ve yakalama listesinde izin verilen modifikatörlerde garip bir asimetri // g ++ -std = c ++ 11 main.cpp -o test_bin; ./test_bin #include <stdio.h> int main () {#if 1 {int param = 0; auto f = [=, & param] (int arg1) değiştirilebilir {param = arg1;}; f (111); printf ("% i \ n", param); } # endif #if 0 {int param = 0; auto f = [&, = param] (int arg1) değiştirilebilir {param = arg1;}; f (111); printf ("% i \ n", param); } #endif dönüş 0; }
bruziuz

Yeni satırın yorumda desteklenmediğine benziyor. Sonra 5.1.2 Lambda ifadelerini açtım, s.88, "Çalışma Taslağı, C ++ Programlama Standardı", Dcoument Number: # 337, 2012-01-16. Ve dilbilgisi sözdizimine baktı. Ve haklısın. "=
Arg

Büyük Teşekkürler, açıklamada sabit ve ayrıca yeni bilgi wrt edin.
bruziuz

16

Lambda işlevi, satır içinde oluşturduğunuz anonim bir işlevdir. Bazılarının açıkladığı gibi değişkenleri yakalayabilir (örn. Http://www.stroustrup.com/C++11FAQ.html#lambda ) ancak bazı sınırlamalar vardır. Örneğin, bunun gibi bir geri arama arayüzü varsa,

void apply(void (*f)(int)) {
    f(10);
    f(20);
    f(30);
}

aşağıda uygulamak için geçilen gibi kullanmak için yerinde bir işlev yazabilirsiniz:

int col=0;
void output() {
    apply([](int data) {
        cout << data << ((++col % 10) ? ' ' : '\n');
    });
}

Ama bunu yapamazsın:

void output(int n) {
    int col=0;
    apply([&col,n](int data) {
        cout << data << ((++col % 10) ? ' ' : '\n');
    });
}

çünkü C ++ 11 standardındaki sınırlamalar. Yakalamaları kullanmak istiyorsanız, kütüphaneye güvenmeniz ve

#include <functional> 

(veya dolaylı olarak almak için algoritma gibi başka bir STL kütüphanesi) ve normal işlevleri aşağıdaki gibi parametre olarak iletmek yerine std :: function ile çalışın:

#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');
    });
}

1
bunun nedeni, lambda'nın yalnızca yakalama özelliği yoksa bir işlev işaretçisine dönüşebilmesidir. eğer applybir functor kabul şablon oldu, işe yarar mıydı
sp2danny

1
Ancak sorun şu ki, eğer uygulama mevcut bir arayüz ise, onu eski ve basit bir fonksiyondan farklı bir şekilde beyan etme lüksüne sahip olamayabilirsiniz. Standart, yakalanan değişkenlere üretilen sabit kodlanmış referanslarla, böyle bir lambda ifadesi her yürütüldüğünde, eski bir düz fonksiyonun yeni bir örneğinin oluşturulmasına izin verecek şekilde tasarlanmış olabilir. Derleme zamanında lambda fonksiyonu oluşturulmuş gibi görünüyor. Başka sonuçları da var. Örneğin, bir statik değişken bildirirseniz, lambda ifadesini yeniden değerlendirseniz bile, yeni bir statik değişken elde etmezsiniz.
Ted

1
işlev işaretçisi genellikle kaydedilmeye yöneliktir ve lambdas yakalama kapsam dışına çıkabilir. sadece yakalama gerektirmeyen lambdaların işlev işaretçilerine dönüştüğü tasarım
gereğiydi

1
Yine de her iki şekilde de aynı nedenle ayrılmış yığın değişkenlerine dikkat etmelisiniz. Bkz. Blogs.msdn.com/b/nativeconcurrency/archive/2012/01/29/… Çıktı ile birlikte yazdığım ve uyguladığım örnek, bunun yerine işlev işaretleyicilerine izin verilir ve kullanılırsa, aynı zamanda da çalışacak şekilde yazılır. Col, tüm işlev çağrıları tamamlanıncaya kadar ayrılmış durumda kalır. Mevcut uygulama arayüzünü kullanarak çalışmak için bu kodu nasıl yeniden yazardınız? Genel veya statik değişkenler mi yoksa kodun daha belirsiz bir dönüşümünü mü kullanırsınız?
Ted

1
ya da belki sadece lambda ifadelerinin değer olduğunu ve bu nedenle geçicidir, ancak kod gelecekte (tekton / statik) sabit kalır, böylece gelecekte çağrılabilir. Bu durumda, istif tahsisli yakalamaları tahsis edildiği sürece işlev ayrılmış olarak kalmalıdır. Tabii ki örneğin bir döngüde fonksiyonun birçok varyasyonu tahsis edilirse dağınık olabilir.
Ted

12

En iyi açıklamalarından biri lambda expressionC ++ Bjarne Stroustrup'un***The C++ Programming Language*** 11 numaralı kitabında ( ISBN-13: 978-0321563842 ) yazarından alınmıştır :

What is a lambda expression?

Bir lambda ifade , bazen de bir şekilde ifade lamda bir şekilde işlev veya (tamamen yanlış olarak, ancak halk dilinde) lambda tanımlanması ve bir kullanma için basitleştirilmiş bir notasyon anonim işlev nesnesini . Bir işleç () ile adlandırılmış bir sınıf tanımlamak, daha sonra o sınıfın bir nesnesini yapmak ve son olarak çağırmak yerine, bir stenografi kullanabiliriz.

When would I use one?

Bu özellikle bir işlemi bir algoritmaya argüman olarak geçirmek istediğimizde kullanışlıdır. Grafik kullanıcı arabirimleri (ve başka yerler) bağlamında, bu tür işlemlere genellikle geri çağrılar denir .

What class of problem do they solve that wasn't possible prior to their introduction?

Burada lambda ifadesi ile yapılan her eylem onlar olmadan çözülebilir, ama çok daha fazla kod ve çok daha büyük karmaşıklık ile sanırım. Lambda ifadesi, kodunuz için optimizasyonun ve onu daha çekici hale getirmenin bir yoludur. Stroustup kadar üzücü:

etkili optimizasyon yolları

Some examples

lambda ifadesi ile

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';
         });
}

veya işlev yoluyla

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'; 
           }
};

ya da

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}); 
}

u gerekiyorsa lambda expressionaşağıdaki gibi adlandırabilirsiniz :

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);
 }

Veya başka bir basit örnek varsayalım

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);
             }
             );
}

sonraki üretecek

0

1

0

1

0

1

0

1

0

1

0 sıralı x - 1; x - 3; x - 4; x - 5; x - 6; x - 7; x - 33;

[]- bu yakalama listesidir veya lambda introducer: lambdasyerel ortamlarına erişim gerektirmezse kullanabiliriz.

Kitaptan alıntı:

Lambda ifadesinin ilk karakteri daima [ . Bir lambda tanıtımı çeşitli şekillerde olabilir:

[] : boş bir yakalama listesi. Bu, lambda gövdesinde çevre bağlamdan hiçbir yerel ismin kullanılamayacağı anlamına gelir. Bu gibi lambda ifadeleri için veriler bağımsız değişkenlerden veya yerel olmayan değişkenlerden elde edilir.

[&] : dolaylı olarak referans ile yakalama. Tüm yerel isimler kullanılabilir. Tüm yerel değişkenlere referans olarak erişilebilir.

[=] : dolaylı olarak değere göre yakalama. Tüm yerel isimler kullanılabilir. Tüm adlar, lambda ifadesinin çağrı noktasında alınan yerel değişkenlerin kopyalarını ifade eder.

[yakalama listesi]: açık yakalama; yakalama listesi, referans olarak veya değere göre yakalanacak yerel değişkenlerin adlarının listesidir (yani, nesnede saklanır). Başında & ile isimleri olan değişkenler referans olarak alınır. Diğer değişkenler değere göre yakalanır. Bir yakalama listesi, bunu ve adlarını ve ardından ... öğelerini içerebilir.

[&, yakalama listesi] : listede belirtilmeyen adlara sahip tüm yerel değişkenleri referans alarak dolaylı olarak yakalar. Yakalama listesi bunu içerebilir. Listelenen adlardan önce & karakteri gelemez. Yakalama listesinde adlandırılan değişkenler değere göre yakalanır.

[=, yakalama listesi] : listede belirtilmeyen adlara sahip tüm yerel değişkenleri dolaylı olarak değere göre yakalar. Yakalama listesi bunu içeremez. Listelenen isimlerden önce & karakteri gelmelidir. Yakalama listesinde adlandırılan değişkenler referans olarak yakalanır.

& Karakterinden önce gelen bir yerel adın her zaman başvuru ile alındığını ve & öğesinde bulunmayan bir yerel adın her zaman değere göre yakalandığını unutmayın. Yalnızca referansla yakalama, çağrı ortamındaki değişkenlerin değiştirilmesine izin verir.

Additional

Lambda expression biçim

resim açıklamasını buraya girin

Ek referanslar:


Güzel açıklama. Döngüler için menzil tabanlı kullanarak lambdalardan kaçınabilir ve kodu kısaltabilirsinizfor (int x : v) { if (x % m == 0) os << x << '\n';}
Dietrich Baumgarten

2

Bulduğum pratik bir kullanım, kazan plakası kodunu azaltmak. Örneğin:

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);
  //... 
}

Lambda olmadan, farklı bsizedurumlar için bir şeyler yapmanız gerekebilir . Elbette bir işlev yaratabilirsiniz, ancak ruh kullanıcı işlevi kapsamındaki kullanımı sınırlamak isterseniz ne olur? lambda'nın doğası bu şartı yerine getirir ve ben bu durum için kullanırım.


2

C ++ 'daki lambda, "hareket halindeyken kullanılabilir" işlevi görür. evet, kelimenin tam anlamıyla hareket halindeyken, siz tanımlarsınız; kullanın; üst işlev kapsamı tamamlandığında lambda işlevi kaybolur.

c ++, c ++ 11'de tanıttı ve herkes mümkün olan her yerde kullanmaya başladı. örnek ve lambda nedir burada bulabilirsiniz https://en.cppreference.com/w/cpp/language/lambda

orada olmayan ama her c ++ programcısı için bilmek gerekli olan açıklayacağım

Lambda'nın her yerde kullanılması amaçlanmamıştır ve her işlev lambda ile değiştirilemez. Ayrıca normal işleve kıyasla en hızlı olanı değildir. çünkü lambda tarafından kullanılması gereken bazı ek yükleri vardır.

bazı durumlarda hat sayısını azaltmaya kesinlikle yardımcı olacaktır. temel olarak, aynı işlevde bir veya daha fazla kez çağrılan kod bölümü için kullanılabilir ve bunun için bağımsız bir işlev oluşturabilmeniz için bu kod parçasına başka bir yerde ihtiyaç duyulmaz.

Aşağıda lambda'nın temel örneği ve arka planda ne var.

Kullanıcı kodu:

int main()
{
  // Lambda & auto
  int member=10;
  auto endGame = [=](int a, int b){ return a+b+member;};

  endGame(4,5);

  return 0;

}

Derleme nasıl genişletir:

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;
}

Gördüğünüz gibi, kullandığınızda ne tür bir ek yük ekliyor. bu yüzden onları her yerde kullanmak iyi bir fikir değil. uygulanabilir olduğu yerlerde kullanılabilir.


evet, kelimenin tam anlamıyla hareket halindeyken, siz tanımlarsınız; kullanın; ve ana fonksiyon kapsamı tamamlandığında lambda fonksiyonu kaybolur .. ya fonksiyon lambda'yı arayana döndürürse?
Nawaz

1
Ayrıca normal işleve kıyasla en hızlı olanı değildir. çünkü lambda tarafından kullanılması gereken bazı ek yükleri vardır. Aslında bu iddiayı desteklemek için herhangi bir kıyaslama yaptınız mı ? Aksine, lambda + şablonları genellikle mümkün olan en hızlı kodu üretir.
Nawaz

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.