Yazılım geliştirme teknikleri: Kısmi Fonksiyon Uygulaması

Adanali

Active member
Yazılım geliştirme teknikleri: Kısmi Fonksiyon Uygulaması


  1. Yazılım geliştirme teknikleri: Kısmi Fonksiyon Uygulaması

Kısmi İşlev Uygulaması veya Kısmi Uygulama, yazılım geliştirmede körlemeye benzer bir tekniktir. İkinci yöntem genellikle işlevsel dillerde kullanılır.







Rainer Grimm, uzun yıllardır yazılım mimarı, ekip lideri ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanır, aynı zamanda sık sık uzmanlık konferanslarında konuşmaktan da keyif alır. Modernes C++ blogunda yoğun bir şekilde C++ tutkusundan bahsediyor.













Birkaç hafta önce blogumun bazı okuyucularıyla tartıştım. Bir okuyucu, kısmi fonksiyonları uygulama hakkında yazmamı önerdi. Başka bir okuyucu, C++’ın Kısmi İşlev Uygulamasını desteklemediğini söyledi. Yanlış. C++, Kısmi İşlev Uygulamalarını birçok varyasyonda destekler. Varyantlar şuna dayalıdır: std::function, std::bind, std::bind_frontlambdalar, auto ve köriler.

Küçük bir teori ile başlayayım.

köri


Kısmi işlevlerin uygulanması, körleme adı verilen bir tekniğe çok benzer. Currying, Haskell gibi işlevsel dillerde iyi bilinen popüler bir deyimdir. Birden fazla bağımsız değişken alan bir işlevin daha sonra yalnızca bir bağımsız değişken alan bir dizi işleve dönüştürüldüğü bir tekniği temsil eder. Bu nedenle, Haskell gibi bir programlama dilinde yalnızca tek argüman fonksiyonları vardır. Sorunuzu duydum: gibi bir işleve sahip olmak nasıl mümkün olabilir? add iki argüman alan uygulama? Sihir dolaylı olarak gerçekleşir. n bağımsız değişken alan bir işlev, yalnızca n -1 bağımsız değişken alan bir işlev döndüren işlevlere dönüştürülür. Bu dökümde ilk madde değerlendirilir.

Körileme adı, matematikçi Haskell Curry ve Moses Schönfinkel tarafından icat edildi. Currying, adını Haskell Curry’nin soyadından alıyor; İsim olarak Haskell. Bazen köriye Schönfinkeln de denir.


İsteğe bağlı işlev bağımsız değişkenlerini değerlendirebileceğiniz için kısmi işlevleri uygulamak, körlemeden daha güçlüdür. C++11, C++’ı desteklediğinden std::function Ve std::bind.

std::bind Ve std::function


std::bind çeşitli şekillerde çağrılabilir öğeler oluşturmanıza olanak tanır. Çağrılabilir öğeler, bir işlev gibi davranan herhangi bir varlıktır. Bunlar özellikle lambda ifadeleri, işlev nesneleri veya işlevlerin kendileridir.

Bir kutu

  • işlev bağımsız değişkenlerini keyfi konumlara bağlama,
  • işlev bağımsız değişkenlerinin sırasını yeniden düzenleyin,
  • işlev bağımsız değişkenleri için yer tutucuları tanıtın,
  • İşlevleri kısmen değerlendirin.
onun dışında

  • yeni çağrılabilir kişiyi doğrudan arayın,
  • çağrılabilir bir Standart Şablon Kitaplığı (STL) algoritmasında kullanın,
  • çağrılabilir std::function kaydetmek.
Bir örnek göstermeden önce kısaca std::function hayal etmek:

  • std::function polimorfik bir fonksiyon sarmalayıcıdır. Herhangi bir çağrılabilir kabul edebilir ve onlara bir isim verebilir. std::function çağrılabilir türünü belirtmeniz gerektiğinde gereklidir.
Şimdi Kısmi Fonksiyon Uygulamasının ilk örneğini sunmak istiyorum:


// bindAndFunction.cpp

#include <functional>
#include <iostream>

double divMe(double a, double b){
return double(a/b);
}

using namespace std::placeholders; // (1)

int main(){

std::cout << 'n';

// invoking the function object directly
std::cout << "1/2.0= " << std::bind(divMe, 1, 2.0)()
<< 'n'; // (2)

// placeholders for both arguments // (3)
std::function<double(double, double)>
myDivBindPlaceholder= std::bind(divMe, _1, _2);
std::cout << "1/2.0= " << myDivBindPlaceholder(1, 2.0) << 'n';

// placeholders for both arguments, swap the arguments (4)
std::function<double(double, double)>
myDivBindPlaceholderSwap= std::bind(divMe, _2, _1);
std::cout << "1/2.0= " << myDivBindPlaceholderSwap(2.0, 1)
<< 'n';

// placeholder for the first argument (5)
std::function<double(double)>
myDivBind1St= std::bind(divMe, _1, 2.0);
std::cout<< "1/2.0= " << myDivBind1St(1) << 'n';

// placeholder for the second argument (6)
std::function<double(double)>
myDivBind2Nd= std::bind(divMe, 1.0, _1);
std::cout << "1/2.0= " << myDivBind2Nd(2.0) << 'n';

std::cout << 'n';

}


Basit gösterime _1, _2 yer tutucular için std::placeholders::_1, std::placeholders::_2 Kaynak kodunda, ad alanını kullanmam gerekiyor std::placeholders 1. satırı girin

Çıktıda 2. satıra bağlıyorum std::bind(divMe, 1, 2.0) işlevin 1 ve 2.0 bağımsız değişkenleri divMe ve onları yerinde arayın. (3, 4, 5 ve 6) benzer bir strateji izler, ancak oluşturulan çağrılabilirleri içerir std::function bir isim ve sonunda onu hatırla. gibi bir model imzası double(double, double) (4) veya double(double) (5 ve 6), çağrılabilir türünü temsil eder std::function kabul edilmiş. double(double, double) iki olan bir çağrılabilir double her birini kabul et double geri gel.

Özellikle son iki örnek (5 ve 6) std::function iki aritmetik alan bir işlev ve arite bir döndüren bir işlev oldukça şaşırtıcıdır. Bir fonksiyonun ariteliği, aldığı bağımsız değişkenlerin sayısıdır.std::bind her iki çağrıda da yalnızca bir bağımsız değişkeni değerlendirin ve değerlendirilmeyen bağımsız değişken için bir yer tutucu kullanın. Bu tekniğe Kısmi İşlev Uygulaması denir.

Son olarak programın çıktısı şu şekildedir:








C++11’de kısmi işlevlere sahip uygulamaları kullanmanın başka bir yolu vardır: lambda ifadeleri.

Lambda ifadeleri


std::bind Ve std::function C++ 11’de neredeyse gereksizdirler. Lambda ifadeleri yerine kullanılabilir std::bind Ve auto neredeyse her zaman yerine std::function kullanım. İşte aktif olan söz konusu program auto ve lambda ifadeleri.


// lambdaAndAuto.cpp

#include <functional>
#include <iostream>

double divMe(double a, double b){
return double(a/b);
}

using namespace std::placeholders;

int main(){

std::cout << 'n';

// invoking the function object directly
std::cout << "1/2.0= " << [](int a, int b)
{ return divMe(a, b); }(1, 2.0) << 'n';

// placeholders for both arguments
auto myDivBindPlaceholder= [](int a, int b)
{ return divMe(a, b); };
std::cout << "1/2.0= " << myDivBindPlaceholder(1, 2.0) << 'n';

// placeholders for both arguments, swap the arguments
auto myDivBindPlaceholderSwap= [](int a, int b)
{ return divMe(b, a); };
std::cout << "1/2.0= " << myDivBindPlaceholderSwap(2.0, 1)
<< 'n';

// placeholder for the first argument
auto myDivBind1St= [](int a){ return divMe(a, 2.0); };
std::cout<< "1/2.0= " << myDivBind1St(1) << 'n';

// placeholder for the second argument
auto myDivBind2Nd= [](int b){ return divMe(1, b); };
std::cout << "1/2.0= " << myDivBind2Nd(2.0) << 'n';

std::cout << 'n';

}


İfade [](int a, int b){ dönüş divMe(a, b); }(1, 2.0), şu şekilde bir lambda ifadesi tanımlar: divMe Yürüt Sondaki parantezler lambda ifadesini yalnızca 1 ve 2.0 argümanlarıyla çağırır. Bununla birlikte, kalan lambda ifadeleri aşağıdaki satırlarda hatırlanır. Bir lambda ifadesi sayesinde, altta yatan işlevin her argümanı bağlanabilir.

Şimdiye kadar kısmi işlev uygulamam var. std::bind ve uygulamalı lambda ifadeleri. C++20’de yeni bir değişken var. std::bind:

std::bind_front


std::bind_front bir çağrılabilir oluşturun. std::bind_front herhangi bir sayıda argümana sahip olabilir ve argümanlarını öne bağlar. Bu neden biz sorusunu akla getiriyor std::bind_front var, çünkü C++11’den beri var std::bind, bu da başlangıçta bağlanabilir. Nedeni basit: Birincisi, std::bind_front kullanımı daha kolay çünkü joker karakterlere ihtiyaç duymuyor ve ikincisi, yayılıyor std::bind_front temeldeki çağrılabilir öğenin istisna belirtimi.

Aşağıdaki program bunu göstermektedir std::bind_front İle ilgili std::bind veya lambda ifadeleri ikame edilebilir.


// bindFront.cpp

#include <functional>
#include <iostream>

int plusFunction(int a, int b) {
return a + b;
}

auto plusLambda = [](int a, int b) {
return a + b;
};

int main() {

std::cout << 'n';

auto twoThousandPlus1 =
std::bind_front(plusFunction, 2000); // (1)
std::cout << "twoThousandPlus1(20): "
<< twoThousandPlus1(20) << 'n';

auto twoThousandPlus2 =
std::bind_front(plusLambda, 2000); // (2)
std::cout << "twoThousandPlus2(20): "
<< twoThousandPlus2(20) << 'n';

auto twoThousandPlus3 =
std::bind_front(std::plus<int>(), 2000); // (3)
std::cout << "twoThousandPlus3(20): "
<< twoThousandPlus3(20) << 'n';

std::cout << "nn";

using namespace std::placeholders;

auto twoThousandPlus4 =
std::bind(plusFunction, 2000, _1); // (4)
std::cout << "twoThousandPlus4(20): "
<< twoThousandPlus4(20) << 'n';

auto twoThousandPlus5 = [](int b)
{ return plusLambda(2000, b); }; // (5)
std::cout << "twoThousandPlus5(20): "
<< twoThousandPlus5(20) << 'n';

std::cout << 'n';

}


Her çağrı (1 – 5), çağrılabilir iki argüman alır ve çağrılabilir tek bir argüman döndürür çünkü ilk argüman 2000’e bağlıdır. Çağrılabilir bir işlev (1), bir lambda ifadesi (2) ve önceden tanımlanmış bir işlev nesnesidir (3). . _1 eksik bağımsız değişkeni gösterir. lambda(5) ifadesi ile doğrudan bir argüman ve bir argüman uygulayabilirsiniz. b eksik parametre için belirtin. Okunabilirlik açısından std::bind_front göre çok daha rahat std::bind veya lambda ifadesi.

Sıradaki ne?


Bağımsız Değişkene Bağlı Arama (ADL), aynı zamanda Koening Araması olarak da bilinir, niteliksiz işlevlerin işlev bağımsız değişkenlerine göre çözülmesine yönelik bir dizi “sihirli” kuraldır.


(rm)



ana sayfaya
 
Üst