Evgenii Legotckoi
Evgenii Legotckoi6. Juli 2018 04:26

Lambda-Funktionen in C++ kochen - Teil 2 - Rekursive Lambda-Funktionen am Beispiel der faktoriellen Berechnung

Im vorherigen Artikel haben wir uns mit der Struktur von Lambda-Funktionen vertraut gemacht, und jetzt werden wir mit Lambdas spielen, die Fakultät berechnen und überlegen, wie eine Lambda-Funktion sein kann dafür verwendet.

Betrachten wir zunächst die übliche Version der Fakultätsrechnung und klären auch, was eine rekursive Funktion ist.

Rekursive Funktion

Eine rekursive Funktion ist eine, die sich selbst aufruft. Das bedeutet, dass es innerhalb der Funktion einen Aufruf an sich selbst gibt und eine unendliche Rekursion stattfinden kann, wenn der Funktionscode keine Bedingungen zum Verlassen der Rekursion enthält.

Hier ist ein Beispiel für eine solche unendlich rekursive Funktion, deren Programm aufgrund eines Aufrufstapelüberlaufs abnormal beendet wird.

#include <iostream>

using namespace std;

void infiniteRecursiveFunction()
{
    cout << "Hello World!" << endl;
    infiniteRecursiveFunction();
}

int main()
{
    infiniteRecursiveFunction();
    return 0;
}

Um dies zu verhindern, müssen Sie eine Bedingung zum Beenden der rekursiven Funktion hinzufügen, z. B. das Erreichen des Zählers für rekursive Aufrufe auf 100.

#include <iostream>

using namespace std;

void infiniteRecursiveFunction(int counter = 0)
{
    cout << "Hello World!" << endl;
    if (counter == 100)
    {
        return;
    }
    infiniteRecursiveFunction(counter + 1);
}

int main()
{
    infiniteRecursiveFunction();
    return 0;
}

Jetzt wird die rekursive Funktion dank des Zählers korrekt abgeschlossen, der Aufrufstapel wird nicht überlaufen und das Programm wird ohne Fehler beendet.

Fakultät

Lassen Sie uns nun definieren, was eine Fakultät ist.

Fakultät ist das Produkt natürlicher Zahlen von 1 bis zur Zahl selbst (einschließlich der gegebenen Zahl).
Die Fakultät wird durch ein Ausrufezeichen „!“ gekennzeichnet.

Beispiele:

  • 4! = 1 · 2 · 3 · 4 = 24
  • 5! = 1 · 2 · 3 · 4 · 5 = 120

Lassen Sie uns nun eine Funktion schreiben, um die Fakultät zu berechnen

long double fact(int N)
{
    if(N < 0) // если пользователь ввел отрицательное число
    {
        return 0; // возвращаем ноль
    }
    else if (N == 0) // если пользователь ввел ноль,
    {
        return 1; // возвращаем факториал от нуля, который равен 1
    }
    else // во всех остальных случаях
    {
        return N * fact(N - 1); // выполняем рекурсивный вызовы функции
    }
}

In diesem Fall wird die Funktion so geschrieben, dass die Fakultätsrechnung von der größten Zahl aus durchgeführt wird und bei Null endet. Dann wird der korrekte Ausstieg aus der Rekursion durchgeführt und die Funktion gibt den Wert der Fakultät zurück.

Als Ergebnis sieht der Programmcode zur Berechnung der Fakultät wie folgt aus

#include <iostream>

using namespace std;

long double fact(int N)
{
    if(N < 0) // если пользователь ввел отрицательное число
    {
        return 0; // возвращаем ноль
    }
    else if (N == 0) // если пользователь ввел ноль,
    {
        return 1; // возвращаем факториал от нуля, который равен 1
    }
    else // во всех остальных случаях
    {
        return N * fact(N - 1); // выполняем рекурсивный вызовы функции
    }
}

int main()
{
    int N {0};
    cout << "Input number for factorial" << endl;
    cin >> N;
    cout << "Factorial for number " << N << " = " << fact(N) << endl; // fact(N) - функция для вычисления факториала.
    return 0;
}

Anwenden einer rekursiven Lambda-Funktion

Und jetzt verwenden wir eine rekursive Lambda-Funktion, um die Fakultät zu berechnen.

In modernen C++-Standards gibt es zwei Möglichkeiten, rekursive Funktionen zu schreiben:

  • Mit std::function
  • Ohne Verwendung von std::function

Verwenden von std::function

In diesem Fall muss die Lambda-Funktion, um Rekursion zu verwenden, ihre eigene Struktur kennen, um sich selbst per Referenz erfassen zu können, aber die Lambda-Funktion ist eine anonyme Objektdeklaration, die irgendwie explizit gemacht werden muss. Dabei hilft uns die std::function, die dabei hilft, die Signatur der Lambda-Funktion zu bestimmen.

#include <iostream>
#include <functional>   // Подключаем библиотеку для использования std::function

using namespace std;

int main()
{
    int N {0};

    // Объявление сигнатуры через std::function -> std::function<int(int)>
    // Сигнатура функции int (int)
    // [&fact] - Захват лямбды самой себя
    std::function<int(int)> fact = [&fact](int N)
    {
        if(N < 0) // если пользователь ввел отрицательное число
        {
            return 0; // возвращаем ноль
        }
        else if (N == 0) // если пользователь ввел ноль,
        {
            return 1; // возвращаем факториал от нуля, который равен 1
        }
        else // во всех остальных случаях
        {
            return N * fact(N - 1); // выполняем рекурсивный вызовы функции
        }
    };

    cout << "Input number for factorial" << endl;
    cin >> N;
    cout << "Factorial for number " << N << " = " << fact(N) << endl; // fact(N) - функция для вычисления факториала.
    return 0;
}

Die Einschränkung rekursiver Lambdas besteht darin, dass wir eine Lambda-Funktion erst erfassen können, wenn ihre Signatur bekannt ist. Das heißt, die sofortige Verwendung von auto funktioniert nicht, da auto die Lambda-Struktur zur Kompilierzeit ableitet und wenn das Lambda-Objekt nicht gebildet wurde, können wir das Lambda nicht erfassen, und da es erfasst wird sich selbst, aber noch nicht kompiliert wurde, dann weiß die Struktur nichts und kann sich daher nicht selbst erfassen.

Dann kommt std::function zur Rettung, mit der Sie die Signatur einer Lambda-Funktion vordefinieren, von dieser Lambda-Funktion initialisiert und von derselben Lambda-Funktion als erfasste Referenz verwendet werden können.

Ohne Verwendung von std::function

Das heißt aber nicht, dass Sie bei rekursiven Funktionen auf die explizite Verwendung von std::function nicht verzichten können. Im C++14 -Standard wurde es möglich, die Argumente von Lambda-Funktionen als auto zu definieren, wodurch die Lambda-Funktion per Referenz als Argument an sich selbst übergeben werden kann. Sie erhalten dieselbe rekursive Lambda-Funktion, verwenden jedoch nur die Tools der Programmiersprache C++.

#include <iostream>

using namespace std;

int main()
{
    int N {0};

    // Объявление лямбды через auto
    // Сигнатура лямбды int (auto&, int)
    // auto& self - в данный аргументы будет передаваться лямбды функция для выполнения самой себя
    auto fact = [](auto& self, int N)
    {
        if(N < 0) // если пользователь ввел отрицательное число
        {
            return 0; // возвращаем ноль
        }
        else if (N == 0) // если пользователь ввел ноль,
        {
            return 1; // возвращаем факториал от нуля, который равен 1
        }
        else // во всех остальных случаях
        {
            // Вызываем лямбду передавая в качестве аргумента лмябду по ссылке дальше в саму себя
            return N * self(self, N - 1); // выполняем рекурсивный вызовы функции
        }
    };

    cout << "Input number for factorial" << endl;
    cin >> N;
    // При первом вызове лямбды также нужно передать в качестве аргумента лямбду в саму себя
    // fact(fact, N)
    cout << "Factorial for number " << N << " = " << fact(fact, N) << endl; // fact(N) - функция для вычисления факториала.
    return 0;
}

Fazit

Zur Frage nach der Zweckmäßigkeit der Verwendung rekursiver Lambda-Funktionen.

Es macht keinen Sinn, eine Funktion oder Methode in einer Klasse zu deklarieren, wenn diese Funktion an einer einzigen Stelle im Code verwendet wird. Dies wird zumindest die Klassenschnittstelle verkomplizieren, wenn wir für jedes Niesen neue Methoden schreiben. Es ist viel besser, das Lambda in einer anderen Methode zu deklarieren, wo es ausgeführt werden soll. Dies gilt für alle Lambda-Funktionen, sowohl regulär als auch rekursiv.

Es ist nicht mehr möglich, eine Lambda-Funktion zu deklarieren und sofort auszuführen. Wenn eine Lambda-Funktion deklariert und sofort aufgerufen wird, ist der Rückgabewert das Ergebnis der Ausführung der Lambda-Funktion und nicht das Lambda-Funktionsobjekt selbst, was bedeutet, dass es nicht funktioniert, das Objekt der Lambda-Funktion zu erfassen, was bedeutet, dass Beide der oben genannten Arten, rekursive Lambda-Funktionen zu schreiben, sind nicht geeignet.

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25. Dezember 2023 10:30
Boost - statisches Verknüpfen im CMake-Projekt unter Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken