Lambda Expression (Lambda ifadeleri) - 1

C++11’le gelen ve yaygın biçimde kullanılan özelliklerden biri de lambda'lardır. Lambda ifadeleri, bir fonksiyon çağrısının yapılacağı yerde doğrudan fonksiyonun kodunu yazabilme olanağını sağlar. Bir lambda ifadesi çağrılabilecek (callable) bir kod birimine karşılık gelir ve derleyici lambda ifade için kendi oluşturduğu bir sınıf türünden bir geçici nesne oluşturacak şekilde kod üretir. Derleyicinin lambda ifadesi karşılığı oluşturduğu sınıf, "closure type" türünden, lambda ifadesi ile oluşturulan geçici nesne de “closure” ya da “closure object” olarak isimlendirilir.

Lambda ifadesinin elemanları


Temel Lambda Sentaksı

   auto l = []() { return "Hello Lambda!..\n"; };

Yukarıdaki kod parçasında auto belirteci kullanılarak l değişkenine lambda ifadesi ile oluşturulan geçici nesne ile ilk değer verilmektedir. Bu durumda l değişkeni derleyicinin lambda ifadesi karşılığında oluşturacağı sınıfın türündendir. Bir lambda ifadesini formülize etmek gerekirse:

[yakalama listesi] (parametre listesi)   ->geri dönüş türü  {lambda ifadesinin gerçekleştireceği kod}

biçiminde ifade edebiliriz. Lambda ifadeleri iki farklı biçimde çağırabiliriz;
  • Fonksiyon çağrı operatörü ile:   l(); 
  • Doğrudan tanımlamanın yapıldığı yerde:  []() { return "Hello Lambda!..\n"; }();

Bir lambda ifadesinin tanımlanması ve çağrılmasına örnek

Lambda'ların Yakalama (Capture Specification) Listesi

Bir lambda ifadesi içinde, o ifadeyi kapsayan bloklarda yer alan dışsal değişkenler özel bir sentaks ile kullanılabilir. Köşeli parantez içerisine yazılan bu sentaks sayesinde kopyalama ya da referans semantiği ile lambda ifadesi içinde kullanılabilir:

Kopyalama yoluyla yakalama [=]

Bu durumda kullanılan tüm dış değişkenler varsayılan şekilde kopyalama ile yakalanır. Her bir değişkenin isminin tek tek yazılmasına gerek kalmaz.

Referans yoluyla yakalama [&]

Bu durumda kullanılan tüm dış değişkenler referans yoluyla yakalanır. Yine her bir değişkenin isminin tek tek yazılmasına gerek kalmaz.
  • [=, &x] : Buradaki = karakteri varsayılan yakalama biçiminin kopyalama olduğunu ifade eder. Bu, x dışında kullanılan tüm isimlerin kopyalama yoluyla yakalanacağı yalnızca x değişkeninin referans yoluyla yakalanacağı anlamındadır.
  • [&, y] : Buradaki & karakteri varsayılan yakalama biçiminin referans olduğunu ifade eder. Bu, y dışında kullanılan tüm isimlerin referans yoluyla yakalanacağı yalnızca y değişkeninin kopyalama yoluyla yakalanacağı anlamına geliyor.
  • [&x, y] : Buradaki x değişkeninin referans yoluyla y değişkeninin kopyalama yoluyla yakalanacağını ifade eder.
Kopyalama ve referans yoluyla yakalama biçimini ele alan bir örnek

Lambda'ların Parametre (Lambda Declarator) Listesi

Parantez içinde tanımladığımız değişkenler derleyicinin yazacağı fonkiyonun parametre değişkenleridir. Lambda ifadesi karşılığı oluşturulacak fonksiyonun parametre değişkeni yok ise parametre parantezi yazılmayabilir.

Lambda ifadesine referans türünde parametre bildirimine bir örnek

Lambda'ların Geri Dönüş Türleri

Derleyici bir lambda ifadesinin geri dönüş değerinin türünü doğrudan belirtilmemmiş ise lambda ifadesinde kullanılan return ifadesinin türünü kabul eder. Yani derleyici lambda ifadesinin geri dönüş değerinin türünün ne olduğunu return ifadesinden bir çıkarım yaparak anlar. Lambda ifadesinde yer alan blok içinde yazılan kodda bir return deyimi yok ise lambda'nın geri dönüş değerinin türü void kabul edilir.
  • [](int x) {return x;}ifadesiyle oluşturulacak lambda ifadesinin geri dönüş değerinin türü int'dir
  • [](int x) ->double {return x;} ifadesiyle oluşturulacak lambda ifadesinin geri dönüş değerinin türü x ifadesinin türü olan int değil doğrudan belirtilen double türündendir. 
Bu noktada lambda ifadelerde doğrudan tür belirtmek için -> syntax'ı kullanılır. Lambda ifadesinin geri dönüş değerinin türünün parametre parantezini izleyen ok (->) atomundan sonra yazıldığını (trailing return type) görebiliriz. Eğer lambda ifadesinin kodu tek bir return deyiminden oluşmuyor ise geri dönüş değeri türünün açıkça yazılması zorunludur. Aksi halde lambda ifadesinin geri dönüş türü void kabul edilir. Ve kod geçersiz duruma düşer.

Lambda ifadesinin constructor'a parametre olarak geçirilmesine bir örnek

Lambda ve STL

Lambda'ların STL kütüphanesinde bulunan fonksiyonlarla kullanımı oldukça pratiktir. Bu nedenle lambda'ların en sık kullanıldığı yer STL algoritmalarına yapılan çağrılardır diyebiliriz. 
Lambda ifadesinin STL algoritmalarıyla birlikte kullanılmasına örnek
Yukarıdaki kodda for_each algoritması birinci ve ikinci parametrelerde Iterator, üçüncü parametrede ise function object istemektedir.

for_each algoritmasının implimantasyonuna bir örnek

Bu noktada biz function objesi olarak lambda ifadesini doğrudan kullanabildik.

Notlar

  • Lambda ifadeleri tamamen aynı olsa bile, derleyici tarafından oluşturulan sınıflar birbirinden farklıdır. Benzer türdendir diyemeyiz.
  • []() -> std::string { return "Hello Lambda!..\n"; }; gibi bir ifadeyi görünce derleyici
class tempclass {
public:
std::string operator()() const
{
return "Hello Lambda!..\n";
}
}; yukarıdaki gibi bir sınıfa dönüştürdüğünü varsayabiliriz.