gpt4 book ai didi

c++ - 如何设计这个易于修改的程序?

转载 作者:可可西里 更新时间:2023-11-01 17:57:28 32 4
gpt4 key购买 nike

这个问题是关于如何设计一个程序,这样就可以很容易地进行某些修改。

我有一个类,它包含一些(非平凡的)数据,并有几个更改这些数据的成员函数。

有时我需要计算此数据的某些属性。但是在每次更改时从头开始重新计算它是很慢的。相反,计算对这些属性的小更新要快得多。

我有几个这样的属性,我需要能够轻松地将它们添加到我的类中或从中删除(或打开/关闭)以进行一些数值实验。该类仅由我自己修改,用于数值模拟(科学代码)。

具体例子

假设我有一个包含数字 x 的类。但我还需要 2^x(x 的“属性”)。基础类是:

class C {
double x;

public:
C() : x(0.0)
{ }

void inc() { x += 1; }
void dec() { x -= 1; }
void set(double x_) { x = x_; }
};

但现在我需要跟踪 2^x 并在每次 x 更改时不断更新此值。所以我最终得到了

class expC {
double expx;

public:
expC(const double &x) {
recompute(x);
}

void inc() { expx *= 2; } // fast incremental change
void dec() { expx /= 2; } // fast incremental change
void recompute(const double &x) {
expx = std::pow(2, x); // slow recomputation from scratch
}
};


class C {
double x;

expC prop1; // XX

public:
C() : x(0.0), prop1(x) // XX
{ }

void inc() {
x += 1;
prop1.inc(); // XX
}
void dec() {
x -= 1;
prop1.dec(); // XX
}
void set(double x_) {
x = x_;
prop1.recompute(x); // XX
}
};

XX 标记我需要对类 C 进行的更改。这是很多变化,很容易出错。它变得更加复杂,有几个属性,我什至相互依赖。

class C {
double x;

expC prop1; // XX
someC prop2; // XX

public:
C() : x(0.0), prop1(x), prop2(x, prop1) // XX
{ }

void inc() {
x += 1;
prop1.inc(); // XX
prop2.inc(); // XX
}
void dec() {
x -= 1;
prop1.dec(); // XX
prop2.dec(); // XX
}
void set(double x_) {
x = x_;
prop1.recompute(x); // XX
prop2.recompute(x, prop1); // XX
}
};

问题:这样一个程序的好的设计是什么?我相信有可能比上面做得更好。目标是:1) 使添加/删除此类属性或打开/关闭它们的计算变得容易 2) 性能至关重要。 incdec 在紧密的内部循环中被调用并且做的事情相对较少。出于性能原因,它们不能虚拟化。

实际上 x 是一个更复杂的数据结构。想想例如在 graph 中添加/删除边缘并在此过程中跟踪其度数序列。


更新

@tobi303 要求我展示如何使用这个类。它的方式类似于:

void simulate(C &c) {
for (/* lots of iterations */) {
c.inc();
double p1 = c.prop1.value();
double p2 = c.prop2.value();
if (condition(p1,p2))
c.dec();
}
}

或者换句话说:

  • 进行(随机)更改
  • 获取更改后的属性值
  • 根据新的属性值,决定是接受还是撤消更改。

它实际上是一个类似于 Metropolis-Hasting algorithm 的蒙特卡洛模拟.

一个具体的例子可能是 C 类中的“数据”(状态)是 Ising model 的自旋状态。 (对于熟悉它的人)和属性是系统的总能量和总磁化强度。在一次旋转翻转后更新这些比从头开始重新计算要快得多。实际上我没有伊辛模型,我有一些更复杂的东西。我有几个属性,有些计算速度快,有些速度慢(实际上我有一些辅助数据结构可以帮助计算属性)。我需要尝试不同属性的组合,因此我经常更改代码中包含的内容。有时我会实现新的属性。当我不需要一个已经实现的属性时,我需要能够出于性能原因关闭它的计算(有些计算速度非常慢)。

最佳答案

只是be lazy并且不要在需要时计算属性。它将删除大量代码和不必要的计算。

当您确实需要您的属性时,如果它不在缓存中则计算它。因此,您需要为每个属性设置一个 bool 值来判断缓存是否是最新的,并且您需要在每次 x 本身更新时使 bool 值无效。

基本上:

class C {
double x;

template <typename Value> struct cachedProp {
bool cache = false;
Value value;
}

cachedProp<expC> prop1;
cachedProp<someC> prop2;
//...

void invalidateCache() {
prop1.cache = false;
prop2.cache = false;
//...
}
public:
expC getProperty1() {
if (!prop1.cache) {
recalculateProp1();
prop1.cache = true;
}
return prop1.value;
}

void inc() {
x += 1;
invalidateCache();
}
};

编辑:一个更懒的解决方案是不在缓存中存储一个 bool 值,而是存储一个与上次更新相对应的整数并在C中维护一个计数器。每次缓存失效时,C 中的计数器都会增加。获取 propX 时,如果计数器与 propX.lastUpdate 不匹配,则更新 `propX。

这样,使缓存失效只是一个操作,不必更新所有属性的缓存。

关于c++ - 如何设计这个易于修改的程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37418954/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com