Giriş
Önceki yazımızda fonksiyon delegelerinden bahsettik. Bu yazımızdan itibaren fonksiyonel programlama yöntemlerini C# ile uygulama yöntemlerinden bahsetmeye başlayacağız.
- Map
- Filter
- Reduce
Map
Steven alttaki tweetiyle Map, Filter ve Reduce yöntemlerini çok güzel şekilde ifade etmiş:
Map/filter/reduce in a tweet:
— Steven Luscher (@steveluscher) June 10, 2016
map([🌽, 🐮, 🐔], cook)
=> [🍿, 🍔, 🍳]
filter([🍿, 🍔, 🍳], isVegetarian)
=> [🍿, 🍳]
reduce([🍿, 🍳], eat)
=> 💩
Cookismindeki fonksiyonumuz Girdi olarak[🌽, 🐮, 🐔]alıyor ve herbirini pişirerek[🍿, 🍔, 🍳]‘e dönüştürüyor.isVegeterianfiltresi[🍿, 🍔, 🍳]arasından vejeteryanler için olmayanları filtreliyor.eatreducefonksiyonu ise[🍿, 🍳]alıp …
Biraz Teori
Hafifçe teorik gidelim:
Elimizde bir f(x) fonksiyonu olsun.
1f(x) = y
x, sıcaklık birimi Celcius olsun ve f(x) fonksiyonu, Celcius birimini
Fahrenheit birimine çeviren bir fonksiyon olduğunda f(x) bir Map (dönüşüm)
fonksiyonu diyebiliriz.
1f(°C) = °F
C# Map Kullanımı
Map fonksiyonunun C# dilindeki karşılığı IEnumerable extension metodu olan
Select fonksiyonudur. Bu yazımızda Select fonksiyonunu kullanmayacağız,
benzerini kendimiz geliştireceğiz.
1IEnumerable<TOut> map<T, TOut>(IEnumerable<T> source, Func<T, TOut> func) {
2 foreach (var element in source)
3 yield return func(element);
4}
Celcius ↔ Fahrenheit Dönüşüm Fonksiyonu
Celcius ↔ Fahrenheit dönüşüm fonksiyonundan yola çıkarak elimizdeki fonksiyonun bir adet girdisi ve bir adet çıktısı olmalı. Alttaki gibi dönüşüm fonksiyonumuz olduğunu farz edelim.
1Fahrenheit convertCtoF (Celcius value) => value * 1.8 + 32.0;
C# Delegelerini Kullanarak Kendi Map Fonksiyonumuzu Yazalım
Önceki yazılarımızda Func<T, TResult> delegesinden bahsetmiştik. Bu dönüşüm
fonksiyonumuzu delege ile temsil etmek istersek:
1Func<Celcius, Fahrenheit> convertor = convertCtoF;
Elimizdeki bir Celcius veri setini, Fahrenheit değerlerine çevirecek bir fonksiyon yazalım ve bunu delegeleri kullanarak yapalım.
Alternatif #1
1Fahrenheit convertCtoF (Celcius value) => value * 1.8 + 32.0;
2
3IEnumerable<Fahrenheit> fahrenheit(IEnumerable<Celcius> values) {
4 foreach(var celcius in values) {
5 yield return convertCtoF(celcius);
6 // ^^^^^^^^^^^
7 }
8}
Buradaki yaklaşım fonksiyonel programlama yöntemlerine uygun olmadı:
fahrenheitfonksiyonu, parametreleri dışındaglobalolan dış bir değişkene (convertCtoF) bağımlı. saf (pure) değil çünkü içeride kullanılan değerler parametrelerden alınmamış. Diğer ifadeylehardcodedbir değer.convertfonksiyonuna direkt bağımlılık mevcut. Diğer ifadeyletightly coupledconvertCtoFfonksiyonu buraya parametre olara geçilebilmeliydi.
Çözüm, fonksiyonları delege olarak tanımlayarak low-level
convertCtoFfonksiyonumuzu high levelfahrenheitfonksiyonumuza parametre olarak geçmek.
Alternatif #2
1IEnumerable<Fahrenheit> fahrenheit(IEnumerable<Celcius> values, Func<Celcius, Fahrenheit> convertor) {
2 foreach(var celcius in values)
3 yield return convertor(celcius);
4}
Yeni fonksiyonumuza Func<Celcius, Fahrenheit> delege tipinde yeni bir parametre ekledik.Böylece bu delege tipine uyumlu herhangi bir fonksiyonu parametre olarak geçebileceğiz.
Kullanımı:
1Func<Celcius, Fahrenheit> convertor = convertCtoF;
2var fahrenheitValues = fahrenheit(celciusValues, convertor);
Genel amaçlı bir map fonksiyonu geliştirelim
Genel amaçlı bir map fonksiyonunu geliştirebilmek için elimizdeki hardcoded olan tip parametrelerini jenerik hale getirmemiz gerekli.
1IEnumerable<TResult> map<T, TResult>(IEnumerable<T> values, Func<T, TResult> convertor) {
2 foreach(var value in values)
3 yield return convertor(value);
4}
Kullanımı:
1Func<Celcius, Fahrenheit> convertor = convertCtoF;
2var fahrenheitValues = map<Celcius, Fahrenheit>(celciusValues, convertor);
3
4// Type parametreleri olmadan da çağırabiliriz.
5var fahrenheitValues = map<Celcius, Fahrenheit>(celciusValues, convertor);
Yeni map fonksiyonumuzla beraber sıcaklık değer listelerini, diğer birimlere
çevirecek fonksiyonları tek tek yazmak yerine map<T, TResult> fonksiyonunu
kullanarak türetebileceğiz.
Örnek:
1var celciusValues = new List<Celcius>() { -40, 0, 100 };
2
3var celciusToFahrenheitValues = map (celciusValues, convertCtoF);
4
5var celciusToKelvinValues = map<Celcius, Kelvin> (new List<Celcius>() { -273, 0, 100 }, value => value + 273.0);
6
7var fahrenheitToCelciusValues = map<Fahrenheit, Celcius> (celciusToFahrenheitValues, value => (value - 32) / 1.8);
Sonuç
Bu yazımızda fonksiyon delegelerini kullanarak genel amaçlı bir map implementasyonu yazmaya çalıştık.
Siz de bir code kata 1 uygulaması olarak ÖTV ve KDV hesaplayan fonksiyonlar yazabilir, bu fonksiyonları delege yardımıyla map fonksiyonu aracılığıyla kullanabilirsiniz.
Örnek bir uygulama içeren LINQPad dosyasını ekte bulabilirsiniz.
Edit
- 2020/12/20 - İş arkadaşım Zişan, LINQ Pad dosyası örneği yerine dotnet fiddle önerdi. Linki aşağıya bırakıyorum, kaydırarak açabilirsiniz.