Evgenii Legotckoi
Evgenii Legotckoi22. August 2019 03:42

C ++ 17 - Lazy-Template-Funktor mit Caching-Berechnungsergebnis für schwere Funktionen

Um die Idee zu entwickeln, das Ergebnis schwerer Funktionsberechnungen zwischenzuspeichern , schlage ich vor, eine kleine Vorlagenklasse zu schreiben, die eine Funktion als Argument nimmt, nämlich eine Lambda-Funktion als das universellste Werkzeug, in dem eine schwere Funktion ausgeführt wird.

In diesem Fall wird das Ergebnis der Funktion nicht zum Zeitpunkt der Erstellung des Functors berechnet, sondern in dem Moment, in dem die Operatorklammer () aufgerufen wird. Und das Ergebnis wird zwischengespeichert. Dadurch können Sie eine schwere Funktion nicht mehr als einmal während der Ausführung einer Methode aufrufen.


Einführung

Natürlich können Sie zuerst eine schwere Funktion ausführen und das Ergebnis in einer Variablen speichern, aber wenn die Logik Ihres Programms es nicht erfordert, dass Sie den gewünschten Wert sofort berechnen, sondern nur, wenn Sie ihn wirklich brauchen. Dann muss diese Funktion überhaupt nicht aufgerufen werden.

Ich werde versuchen, ein Beispiel zu zeigen, wo dies nützlich sein kann.

Zum Beispiel gibt es in diesem künstlichen Beispiel_, das eindeutig ein Refactoring erfordert, Verzweigungen, wenn eine schwere Funktion mehrmals aufgerufen werden muss, wenn sie nur einmal aufgerufen werden muss, und wenn sie überhaupt nicht aufgerufen werden muss.

int calc(int cases, int cases_2, int base)
{
    int result = 0;

    switch (cases)
    {
        case 0:
        {
            result = 3 * heavy_calc(base);
            break;
        }
        case 1:
        {
            result = 4;
            break;
        }
        case 2:
        {
            result = 2 * heavy_calc(base);
            break;
        }
        case 3:
        {
            result = 3 * heavy_calc(base);
            if (cases_2 < 2)
            {
                result += heavy_calc(base) / 2;
            }
            break;
        }
        default:
            return heavy_calc(base);
    }

    switch (cases_2)
    {
        case 0:
        {
            result = result * heavy_calc(base);
            break;
        }
        case 1:
        {
            result += result;
            break;
        }
        case 2:
        {
            return result - 1;
        }
        default:
            return 2 * heavy_calc(base);
    }
    return result;
}

Eine mögliche Lösung wäre, diese Funktion einmal ganz am Anfang aufzurufen und das Ergebnis in einer Variablen zu speichern, aber dann werden wir die Ausführung des Codes in den Zweigen verlangsamen, in denen kein schwerer Funktionsaufruf erforderlich ist. Daher lohnt es sich, darüber nachzudenken, wie man den Code so umschreibt, dass eine schwere Funktion nur einmal aufgerufen wird.

Lazy Template-Funktor mit Ergebnis-Caching

Dazu schlage ich vor, eine Funktor-Template-Klasse zu schreiben.

BEACHTUNG!!! Dieser Code funktioniert nur, wenn Standard-C++17 oder neuer verwendet wird

#ifndef LAZYCACHEDFUNCTION_H
#define LAZYCACHEDFUNCTION_H

#include <type_traits>

// The template, as a template parameter, will take the type of function to be called
// and will print the return type via std :: result_of_t from the C++17 standard
template <typename T, typename CachedType = typename std::result_of_t<T()>>
class LazyCachedFunction
{
public:
    // The constructor of the functor takes as argument the function to be called
    // and move it to the m_function member
    template<typename T>
    explicit inline LazyCachedFunction(T&& function) :
        m_function(std::forward<T>(function))
    {
    }

    // Do the operator overload of parentheses,
    // so that calling a function inside a functor is like calling a regular function
    auto operator()() const
    {
        // Check that the cache exists
        if (!m_initialized)
        {
            // if not, then call the heavy function
            m_value = m_function();
            m_initialized = true;
        }
        // We return the result of calculations from the cache
        return m_value;
    }

private:
    T m_function;                       ///< function called
    mutable CachedType m_value;         ///< cached result
    mutable bool m_initialized {false}; ///< initialization flag, which indicates that the cache is full
};

#endif // LAZYCACHEDFUNCTION_H

Beispiel

Und jetzt verwenden wir diesen Funktor

#include <iostream>
#include <string>

#include "lazycachedfunction.h"

using namespace std;

int heavy_calc(int base)
{
    cout << "heavy_calc" << std::endl;
    // sleep(+100500 years)
    return base * 25;
}

void calclulateValue(int base)
{
    // Wrap heavy function i lambda function
    // this is the most convenient universal option
    auto foo = [&]()
    {
        return heavy_calc(base);
    };
    // We need to pass the function type to the template parameter, so we need to use decltype
    LazyCachedFunction<decltype(foo)> lazyCall(foo);
    // Next, call lazyCall() twice and see that the heavy function is called only once
    int fooFoo = lazyCall() + lazyCall();
    cout << fooFoo << std::endl;
}

int main()
{
    // Let's double check by double calling calclulateValue
    calclulateValue(1);
    calclulateValue(10);
    return 0;
}

Konsolenausgabe

heavy_calc
50
heavy_calc
500

Überprüfen Sie, dass die Funktion nicht aufgerufen wird, wenn wir den Klammeroperator im Funktor nicht aufrufen

Für eine solche Überprüfung können wir die Funktion computeValue wie folgt ändern

void calclulateValue(int base)
{
    auto foo = [&]()
    {
        return heavy_calc(base);
    };
    LazyCachedFunction<decltype(foo)> lazyCall(foo);
    int fooFoo = 10;
    cout << fooFoo << std::endl;
}

Die Ausgabe an die Konsole sieht wie folgt aus

10
10

Was beweist, dass die Funktion nicht aufgerufen wird, wenn sie nicht benötigt wird.

Dementsprechend kann ein solcher Funktor in dem Fall, in dem es nicht immer notwendig ist, einige schwere Berechnungen durchzuführen, das Programm etwas optimieren.
Ohne die Struktur der zuvor geschriebenen Methode stark zu beeinflussen, trägt es auch dazu bei, den Code lesbar zu halten.

Рекомендуємо хостинг 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
A
ALO1ZE19. Oktober 2024 08:19
Fb3-Dateileser auf Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов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> в заголовочном файле не работает валидатор.
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