Functor'lar ve Lambda İfadeleri
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 74 / 104.
Birçok STL algoritması, davranışlarını özelleştiren çağrılabilir bir nesne kabul eder. std::transform ile kullanılan lambdaları zaten gördünüz. C++'ta çağrılabilir nesneler oluşturmanın iki yolu olan hem functor'ları hem de lambda ifadelerini inceleyelim.
Bir functor (fonksiyon nesnesi), operator() operatörünü aşırı yükleyerek örneklerin fonksiyonlar gibi çağrılmasına olanak tanıyan bir sınıftır:
#include <algorithm>
#include <vector>
#include <iostream>
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
int main() {
std::vector<int> nums = {1, 2, 3, 4};
std::vector<int> result(nums.size());
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));
// sonuç: {3, 6, 9, 12}
}Her bir funktor örneği, oluşturma sırasında atanan üye değişkenler aracılığıyla kendi bağımsız durumunu taşıyabilir; bu, sıradan bir fonksiyon çağrısının yapamayacağı bir şeydir. Ancak, basit işlemler için bir sınıf tanımlamak çok fazla kod gerektirir. Lambda ifadeleri kısa ve öz bir alternatif sunar:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });Lambda söz dizimi [capture](parameters) { body } şeklindedir. Yakalama (capture) ifadesi, lambdanın hangi dış değişkenlere erişebileceğini belirtir. Hepsini değere göre yakalamak için [=], hepsini referansa göre yakalamak için [&] kullanın veya [factor] ya da [&factor] gibi belirli değişkenleri listeleyin.
Lambda'lar, özel kriterlere göre sıralama gibi algoritmalarla yapılan tek seferlik işlemler için özellikle kullanışlıdır:
std::vector<int> nums = {5, -2, 8, -1};
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });
// Mutlak değere göre sıralanmış hali: {-1, -2, 5, 8}Görev
KolayÜrün fiyatlarına farklı indirim stratejileri uygulamak için hem funktorları (functors) hem de lambda ifadelerini gösteren bir fiyat hesaplayıcı oluşturalım.
Kodunuzu iki dosya halinde düzenleyeceksiniz:
Discounts.h: İndirim funktorlarınızı ve yardımcı fonksiyonlarınızı burada tanımlayın.Bir indirim yüzdesini (tam sayı olarak) saklayan
PercentageDiscountadında bir funktor oluşturun. Bu funktorunoperator()fonksiyonu birdoublefiyat almalı ve indirimli fiyatı döndürmelidir. Örneğin, 100$ üzerindeki %20'lik bir indirim 80$ döndürmelidir.Çıkarılacak sabit bir tutarı (bir
doubleolarak) saklayanFixedDiscountadında başka bir funktor oluşturun. Bu funktorunoperator()fonksiyonu bir fiyat almalı ve fiyattan sabit tutarı çıkararak sonucu döndürmelidir (ancak sonuç asla 0'ın altına düşmemelidir).Bir
const std::vector<double>&alan ve tüm fiyatları aralarında boşluk bırakarak yazdıran, ardından bir alt satıra geçenprintPricesadında bir fonksiyon oluşturun. Her fiyatı virgülden sonra iki basamak olacak şekilde formatlayın.main.cpp: Beş girişi okuyun (her biri ayrı bir satırda):- Birinci ürün fiyatı (double)
- İkinci ürün fiyatı (double)
- Üçüncü ürün fiyatı (double)
- Uygulanacak yüzde indirimi (tam sayı, örneğin %20 için 20)
- Sabit indirim tutarı (double)
Üç fiyatı içeren bir vektör oluşturun ve her iki yaklaşımı da gösterin:
Original prices:yazdırın ve ardından fiyatları listeleyin- Yeni bir indirimli fiyatlar vektörü oluşturmak için
PercentageDiscountfunktorunuzla birliktestd::transformkullanın.After percentage discount:yazdırın ve ardından sonuçları listeleyin - Orijinal fiyatlar üzerinde başka bir vektör oluşturmak için
FixedDiscountfunktorunuzla birliktestd::transformkullanın.After fixed discount:yazdırın ve ardından sonuçları listeleyin - Her bir orijinal fiyatı iki katına çıkaran bir lambda ifadesi ile
std::transformkullanın.Premium prices (doubled):yazdırın ve ardından sonuçları listeleyin - Orijinal fiyatları büyükten küçüğe sıralamak için bir lambda ile
std::sortkullanın.Sorted (high to low):yazdırın ve ardından sıralanmış fiyatları listeleyin
Örneğin, 100.00, 50.00, 75.00, 20 ve 15.00 girişleri ile:
Original prices: 100.00 50.00 75.00
After percentage discount: 80.00 40.00 60.00
After fixed discount: 85.00 35.00 60.00
Premium prices (doubled): 200.00 100.00 150.00
Sorted (high to low): 100.00 75.00 50.00 Bu meydan okuma, funktorları (indirim tutarı gibi durumları koruyan) lambdalarla (hızlı, satır içi işlemler için değişkenleri yakalayan) karşılaştırmanıza olanak tanır. Her iki yaklaşım da std::transform ve std::sort gibi STL algoritmalarıyla sorunsuz bir şekilde çalışır.
Kendin dene
#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"
int main() {
// Girdileri oku
double price1, price2, price3;
int percentageDiscount;
double fixedDiscount;
std::cin >> price1;
std::cin >> price2;
std::cin >> price3;
std::cin >> percentageDiscount;
std::cin >> fixedDiscount;
// Üç fiyatı içeren bir vektör oluştur
std::vector<double> prices = {price1, price2, price3};
// TODO: "Original prices:" yazdır ve ardından printPrices kullanarak fiyatları listele
// TODO: PercentageDiscount funktoru ile std::transform kullan
// Sonuçlar için yeni bir vektör oluştur
// "After percentage discount:" yazdır ve ardından sonuçları listele
// TODO: Orijinal fiyatlar üzerinde FixedDiscount funktoru ile std::transform kullan
// Sonuçlar için yeni bir vektör oluştur
// "After fixed discount:" yazdır ve ardından sonuçları listele
// TODO: Her bir orijinal fiyatı ikiye katlayan bir lambda ile std::transform kullan
// Sonuçlar için yeni bir vektör oluştur
// "Premium prices (doubled):" yazdır ve ardından sonuçları listele
// TODO: Orijinal fiyatları azalan düzende sıralamak için bir lambda ile std::sort kullan
// "Sorted (high to low):" yazdır ve ardından sıralanmış fiyatları listele
return 0;
}Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner