Also beantwortete ich eine Frage über faule Bewertung (here, meine Antwort ist Overkill für diesen Fall, aber die Idee scheint interessant) und es ließ mich darüber nachdenken, wie faul Auswertung in C++ getan werden könnte . Ich hatte einen Weg gefunden, aber ich war mir nicht sicher, welche Fallstricke dabei auftraten. Gibt es andere Möglichkeiten, eine faule Bewertung zu erreichen? Wie könnte das geschehen? Was sind die Fallstricke und dieses und andere Designs?Ein Weg zur Lazy Evaluation in C++
Hier ist meine Idee:
#include <iostream>
#include <functional>
#include <memory>
#include <string>
#define LAZY(E) lazy<decltype((E))>{[&](){ return E; }}
template<class T>
class lazy {
private:
typedef std::function<std::shared_ptr<T>()> thunk_type;
mutable std::shared_ptr<thunk_type> thunk_ptr;
public:
lazy(const std::function<T()>& x)
: thunk_ptr(
std::make_shared<thunk_type>([x](){
return std::make_shared<T>(x());
})) {}
const T& operator()() const {
std::shared_ptr<T> val = (*thunk_ptr)();
*thunk_ptr = [val](){ return val; };
return *val;
}
T& operator()() {
std::shared_ptr<T> val = (*thunk_ptr)();
*thunk_ptr = [val](){ return val; };
return *val;
}
};
void log(const lazy<std::string>& msg) {
std::cout << msg() << std::endl;
}
int main() {
std::string hello = "hello";
std::string world = "world";
auto x = LAZY((std::cout << "I was evaluated!\n", hello + ", " + world + "!"));
log(x);
log(x);
log(x);
log(x);
return 0;
}
Einige Dinge, die ich in meinem Design besorgt war.
- decltype hat einige seltsame Regeln. Hat meine Verwendung von decltype irgendwelche Probleme? Ich fügte zusätzliche Klammern um das E im LAZY-Makro hinzu, um sicherzustellen, dass einzelne Namen fair behandelt wurden, wie Referenzen wie vec [10]. Gibt es andere Dinge, die ich nicht vertrete?
- In meinem Beispiel gibt es viele Ebenen der Indirektion. Es scheint, dass dies vermeidbar sein könnte.
- Ist dies korrekt memoizing das Ergebnis, so dass egal, was oder wie viele Dinge Bezug auf den faulen Wert, wird es nur einmal bewerten (dieser bin ich ziemlich zuversichtlich, aber faule Auswertung plus Tonnen von geteilten Zeigern könnte mich werfen Schleife)
Was sind Ihre Gedanken?
Klingt ein bisschen wie Sie versuchen zu imitieren 'std :: future' und' std :: async' mit 'std :: Start :: deferred' ... – MFH
Außer es ist nicht asynchron. Ich versuche, D's 'Lazy' Keyword nachzuahmen und Memoization wie in Sprachen wie Haskell hinzuzufügen. – Jake
'std :: lauch :: deferred' ist eigentlich faul ausgewertet ... Es wird auf den ersten Thread ausgewertet, der versucht, auf die Zukunft zuzugreifen. – MFH