Menu
Coddy logo textTech

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}
challenge icon

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 PercentageDiscount adında bir funktor oluşturun. Bu funktorun operator() fonksiyonu bir double fiyat almalı ve indirimli fiyatı döndürmelidir. Örneğin, 100$ üzerindeki %20 indirim 80$ döndürmelidir.

    Çıkarılacak sabit bir tutarı (bir double olarak) saklayan FixedDiscount adında başka bir funktor oluşturun. Bu funktorun operator() 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çen printPrices adı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):
    1. Birinci ürün fiyatı (double)
    2. İkinci ürün fiyatı (double)
    3. Üçüncü ürün fiyatı (double)
    4. Uygulanacak yüzde indirimi (%20 için 20 gibi bir tam sayı)
    5. Sabit indirim tutarı (double)

    Üç fiyatı içeren bir vektör oluşturun ve her iki yaklaşımı da gösterin:

    1. Original prices: yazdırın ve ardından fiyatları listeleyin
    2. İndirimli fiyatlardan oluşan yeni bir vektör oluşturmak için PercentageDiscount funktorunuzla birlikte std::transform kullanın. After percentage discount: yazdırın ve ardından sonuçları listeleyin
    3. Orijinal fiyatlar üzerinde başka bir vektör oluşturmak için FixedDiscount funktorunuzla birlikte std::transform kullanın. After fixed discount: yazdırın ve ardından sonuçları listeleyin
    4. Her bir orijinal fiyatı iki katına çıkaran bir lambda ifadesi ile std::transform kullanın. Premium prices (doubled): yazdırın ve ardından sonuçları listeleyin
    5. Orijinal fiyatları azalan düzende sıralamak için bir lambda ile std::sort kullanı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;
}
quiz iconKendini test et

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