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 nesneyi 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 örneklerinin 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));
// result: {3, 6, 9, 12}
}Funktörler, normal fonksiyonların yapamadığı şekilde çağrılar arasında durum koruyabilirler. Ancak, basit işlemler için bir sınıf tanımlamak oldukça uzundur. 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. Her şeyi değere göre yakalamak için [=], her şeyi referansa göre yakalamak için [&] kullanın veya [factor] veya [&factor] gibi belirli değişkenleri listeleyin.
Lambdalar, ö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 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ş girdi 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 (%20 için 20 gibi bir tam sayı)
- 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- İndirimli fiyatlardan oluşan yeni bir 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ı azalan düzende 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 girdileriyle:
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 zorluk, 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.
Kopya kağıdı
STL algoritmaları, davranışlarını özelleştirmek için çağrılabilir nesneleri kabul eder. İki yaygın yaklaşım functor'lar ve lambda ifadeleridir.
Bir functor, operator() operatörünü aşırı yükleyen ve örneklerinin fonksiyonlar gibi çağrılmasına olanak tanıyan bir sınıftır:
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));Functor'lar çağrılar arasında durum koruyabilirler, ancak ayrıntılı sınıf tanımları gerektirirler.
Lambda ifadeleri, [capture](parameters) { body } sözdizimi ile kısa ve öz bir alternatif sunar:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });Yakalama (capture) yan tümcesi, lambdanın hangi dış değişkenlere erişebileceğini belirtir:
[=]- tümünü değer ile yakala[&]- tümünü referans ile yakala[factor]- belirli bir değişkeni değer ile yakala[&factor]- belirli bir değişkeni referans ile yakala
Lambdalar, özel sıralama kriterleri için std::sort gibi algoritmalarla iyi çalışır:
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });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 vector oluştur
std::vector<double> prices = {price1, price2, price3};
// TODO: "Original prices:" yazdır ve ardından printPrices kullanarak fiyatları listele
// TODO: PercentageDiscount functor'ı ile std::transform kullan
// Sonuçlar için yeni bir vector oluştur
// "After percentage discount:" yazdır ve ardından sonuçları listele
// TODO: Orijinal fiyatlar üzerinde FixedDiscount functor'ı ile std::transform kullan
// Sonuçlar için yeni bir vector oluştur
// "After fixed discount:" yazdır ve ardından sonuçları listele
// TODO: Her bir orijinal fiyatı iki katına çıkaran bir lambda ile std::transform kullan
// Sonuçlar için yeni bir vector 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