考虑以下代码(使用gcc 4.7.2编译):
#include<iostream>
#include<memory>
struct Base
{
~Base() {}
virtual double operator()(double x) const = 0;
};
template<typename F, typename G> struct Compose;
struct A : public Base
{
virtual double operator()(double x) const {return x;} //suppose this is a hard-to.calculate function, lots of temporaries, maybe also time-dependent, etc
template <typename F>
Compose<A,F> operator()(const F& f) const {return Compose<A,F>(*this,f);}
};
struct B : public Base
{
virtual double operator()(double x) const {return x*x;} //suppose this is a hard-to.calculate function, lots of temporaries, maybe also time-dependent, etc
template <typename F>
Compose<B,F> operator()(const F& f) const {return Compose<B,F>(*this,f);}
};
struct C : public Base
{
virtual double operator()(double x) const {return x*x*x;} //suppose this is a hard-to.calculate function, lots of temporaries, maybe also time-dependent, etc.
template <typename F>
Compose<C,F> operator()(const F& f) const {return Compose<C,F>(*this,f);}
};
template<typename F, typename G>
struct Compose : public Base
{
Compose(const F &_f, const G &_g) : f(_f), g(_g) {}
F f;
G g;
virtual double operator()(double x) const {return f(g(x));}
};
int main()
{
double x=2.0;
A a;
B b;
C c;
std::shared_ptr<Base> ptrBase = std::make_shared<A>(A());
std::shared_ptr<A> ptrA = std::make_shared<A>(a);
std::cout<< a(x) <<std::endl;
std::cout<< ptrBase->operator()(x) <<std::endl;
std::cout<< ptrA->operator()(x) <<std::endl;
std::cout<< b(a(x)) <<std::endl;
std::cout<< c(b(a(x))) <<std::endl;
std::cout<< a(c(b(a(x)))) <<std::endl;
std::cout<< ptrA->operator()(c(b(ptrBase->operator()(x)))) <<std::endl;
}
输出:
2
2
2
4
64
64
64
好的,一个简短问题的长期前奏。这里发生了几个冗余计算,例如,A()(x)的结果被计算六次(一些通过对象,一些通过指针)。如何实现存储已评估函数值的全局输出数组?
我想到的一种方法应该是为每个(组合的)类实现唯一的函数std::string id()
,加上一个全局std::map<std::string,double>
来存储结果,然后检查是否(结果存在。
你在这里看到其他可能更好的方法吗?提前谢谢。
很抱歉,虽然有一些相似之处(“memoization”这个词似乎在某种程度上触发反射),但我真的不明白为什么这应该重复......但我愿意讨论。
在我看来,上述案例比链接线程中的案例复杂得多(例如,它不仅仅是斐波纳契函数)。而且,到目前为止,我可以看到突出显示的memoization类将以不同的方式处理对象和指针(至少不进行进一步编辑)。我的目的是得出一种模式,其中每个结果只计算一次,考虑如何调用它。
到目前为止,我正在使用CRTP的静态结果类,这导致以下代码(使用gcc 4.7.2编译。):
#include<iostream>
#include<memory>
#include<string>
#include<map>
struct Base
{
virtual ~Base() {}
virtual double operator()(double x) const = 0;
virtual std::string id() const = 0;
};
template<typename F, typename G> struct Compose;
template<typename T>
struct Result
{
virtual ~Result() {}
double get(double x) const
{
return mem.find(x)->second;
}
bool isSet(double x) const {it=mem.find(x); return it!=mem.end();}
//get the previously found result by isSet(x)
double get() const
{
return it->second;
}
protected:
//the set function is const, as it works only on static variables
//don't know whether it is the best idea, but it allows for a const operator() later...
void set(double x, double y) const
{
mem.insert(std::make_pair(x,y));
}
private:
static std::map<double, double> mem;
static std::map<double, double>::const_iterator it;
};
template<typename T> std::map<double, double> Result<T>::mem;
template<typename T> std::map<double, double>::const_iterator Result<T>::it=Result<T>::mem.begin();
struct A : public Base, public Result<A>
{
virtual double operator()(double x) const
{
if(isSet(x))
{
return get();
}
else
{
double y=x;
set(x,y);
std::cout<<"setA ";
return y;
}
}
template <typename F>
Compose<A,F> operator()(const F& f) const {return Compose<A,F>(*this,f);}
virtual std::string id() const {return "A";}
};
struct B : public Base, public Result<B>
{
virtual double operator()(double x) const
{
if(isSet(x))
{
return get();
}
else
{
double y=x*x;
set(x,y);
std::cout<<"setB ";
return y;
}
}
template <typename F>
Compose<B,F> operator()(const F& f) const {return Compose<B,F>(*this,f);}
virtual std::string id() const {return "B";}
};
struct C : public Base, public Result<C>
{
virtual double operator()(double x) const
{
if(isSet(x))
{
return get();
}
else
{
double y=x*x*x;
set(x,y);
std::cout<<"setC ";
return y;
}
}
template <typename F>
Compose<C,F> operator()(const F& f) const {return Compose<C,F>(*this,f);}
virtual std::string id() const {return "C";}
};
template<typename F, typename G>
struct Compose : public Base, public Result<Compose<F,G> >
{
Compose(const F &_f, const G &_g) : f(_f), g(_g) {}
F f;
G g;
virtual double operator()(double x) const
{
if(this->isSet(x))
{
return this->get();
}
else
{
double y=f(g(x));
this->set(x,y);
std::cout<<"set"<<this->id()<<" ";
return y;
}
}
virtual std::string id() const {return f.id() + "(" + g.id() + ")";}
};
int main()
{
double x=2.0;
A a;
B b;
C c;
std::shared_ptr<Base> ptrBase = std::make_shared<A>(A());
std::shared_ptr<A> ptrA = std::make_shared<A>(A());
std::cout<<"-------------------------------"<<std::endl;
std::cout<< a(x) <<std::endl;
std::cout<<ptrBase->operator()(x) <<std::endl;
std::cout<<ptrA->operator()(x) <<std::endl;
std::cout<<"-------------------------------"<<std::endl;
std::cout<<c(x)<<std::endl;
std::cout<<C()(x)<<std::endl;
std::cout<<C()(x)<<std::endl;
std::cout<<"-------------------------------"<<std::endl;
auto ba= b(a);
std::cout<<ba(x) << std::endl;
auto cba= c(ba);
std::cout<<cba(x)<< std::endl;
auto acba= a(cba);
std::cout<<acba(x)<<std::endl;
}
输出:
-------------------------------
setA 2
2
2
-------------------------------
setC 8
8
8
-------------------------------
setB setB(A) 4
setC(B(A)) 64
setA(C(B(A))) 64
-------------------------------
好的,有些事情需要注意:
通过从静态结果类继承,有趣的是所有不同的称为A(通过对象,通过指针到对象,通过指针到基础)检索存储的结果(只有一个)组)。对于C()也是如此,它被调用三次但只设置一次。
为了预测下一次反射,我知道多重继承可能很糟糕,但这些类似乎很好地分开了。组合可能会获得相同的行为,所以请忘记这一点。
事实上,这还不是一个真正的记忆类,因为它只是存储首先计算的结果。因此它也会给出错误的结果[例如。对于C(B(A))]。但是,这可以通过地图或其他方式轻松治愈。我最后会这样做,目前只关于模式(EDIT:已完成)。
作为最后一点,我知道与看似重复的线程不同的解决方案并不意味着不同的问题。然而,它可能( - 希望)在这个页面上产生一些扩展的memoization-topic,所有人都可以从中受益。
我期待着你的想法。提前谢谢!
编辑2:帮助自己是一件好事,现在代码正如我所寻找的那样。我在上面的帖子中更新了它。
的变化:
添加了一个地图,以实际制作一个真正的memoization类。注意:双重比较是危险的,因此最好通过一个更合适的比较函数而不是标准的相等。
将结果数组的静态成员设为私有,并且设置者受到保护,因为只有函数类可能会更改结果。
模板参数不会给出地图类型(如在链接的线程中)。这是我不需要的开销(也可能是YAGNI)。
现在我不再和自己说话了。评论仍然受欢迎。谢谢,大卫