8

我有几个指标需要“始终保持最新”。即当有任何改变时,我需要重新计算“依赖”。我有几个级别,只有在计算上一个级别时才应该计算每个下一个级别。让我通过这张闪亮的图片来解释:

设计

在某个时候假设法郎发生了变化。那么我们应该:

  1. 计算法郎 / 第纳尔
  2. 计算法郎 / 第纳尔 / 比索

或者,如果同时更改比索、法郎和第纳尔,那么我们应该:

  1. 计算法郎 / 第纳尔
  2. 计算法郎 / 第纳尔 / 比索
  3. 计算比索 + 欧元 / (欧元 + 美元)

因此,无论何时发生任何Level 0变化,我们都应该重新计算所有其他级别。但

  • 我们应该只计算必需的项目。如果欧元改变,我们不需要重新计算法郎/第纳尔
  • 我们不应该计算任何东西超过一次。如果欧元和美元同时更改,我们应该只计算一次欧元 + 美元(而不是两次)。

最直接的解决方案是:

  • 将每个级别存储在数组中
  • 对于数组中的每个项目跟踪来自下一级的“听众”(可能很困难,因为例如比索有来自不同级别的听众 - 来自 Level2 的 Franc / Dinar / Peso 和来自 Level 3 的 Peso + Euro /(Euro + Usd),所以两个 -需要维度数组..)
  • 如果项目被重新计算,则标记所有它的侦听器也被重新计算
  • 从 0 级到最后一级并重新计算标记为要重新计算的项目(最初更新的项目是要重新计算的市场,例如比索)。

我想我的问题是众所周知的,也许你可以建议我一般的众所周知的解决方案。我不想重新发明轮子:) 谢谢!

4

4 回答 4

2

我认为基于级别的方法是不错的,假设听众总是处于较低的级别。

想法:

有一个包含实际数据的二维数组,第一个索引是级别,第二个是级别上的位置。让每个元素都有一个willBeRecalculated标志。

每个级别都有一个toBeRecalculated列表(所以是一个列表数组)。

对于每个元素,都有一个包含 2 个整数的元素(监听器)列表 - 一个用于级别,一个用于索引。

对于要修改的每个元素,将元素添加到toBeRecalculated适当的级别并设置willBeRecalculated为 true。

然后toBeRecalculated从第一层到最后一层,重新计算每个元素,将其设置willBeRecalculated为 false,并且对于每个侦听器,查找适用的元素,如果willBeRecalculated为 true,则不执行任何操作,否则,设置willBeRecalculated为 true 并将其添加到toBeRecalculated其 (听众的)水平。

这种方法不会遍历所有数据来检查需要修改/已修改的内容,它只检查适用的元素,并且没有重复计算。

例子:

为了这:

(对于我的缩写,我只是取了每个单词的第一个字母。我使用的是 0 索引数组)

实际数据:

[[E, U, P, F, D],
 [E+U, F/D],
 [E/E+D, F/D/P],
 [P+E/E+U]
]

听众:

E:[(1,0), (2,0)] // E+U and E/E+U
U:[(1,0)] // E+U
P:[(2,1), (3,0)]
F:[(1,1)]
D:[(1,1)]
E+U:[(2,0)]
F/D:[(2,1)]
E/E+U:[(3,0)]

修改EU

两者都添加E和设置U为真。toBeRecalculated[0]willBeRecalculated

通过toBeRecalculated[0]

修改时E,将其设置willBeRecalculated为false,将E+U's设置willBeRecalculated为true并添加到toBeRecalculated[1],将E/E+U's设置willBeRecalculated为true并添加到toBeRecalculated[2]

修改 时U,将其设置willBeRecalculated为 false ,我们检查E+U'swillBeRecalculated并查看它是否为 true,所以什么也不做。

然后通过toBeRecalculated[1]。修改 时E+U,将其设置willBeRecalculated为 false 并检查E/E+U'swillBeRecalculated并查看它是否为 true,所以什么也不做。

笔记:

让侦听器成为指向元素的指针而不是级别和索引变量可能会更好。

于 2013-07-18T14:19:51.243 回答
2

好吧,当你说你在处理关卡时,你会想到某种树数据结构。

但是对于您的问题,我认为可以对某种有向无环图进行建模。

您的图表可能看起来像这样(所有方向都向下)..

                             root 
                   /     /     |     \     \
                 E      U      P     F      D
                 \     /
                  \   /
              (Euro + Usd)

如果您像遍历树数据结构一样遍历它,则每次更新货币时,您将只更新一次转换率。

于 2013-07-18T14:28:18.930 回答
0

您所描述的内容可以很容易地用反应式编程语言来完成。

Qt 的 QML 还提供了一种属性绑定机制,可以为 UI 实现这一点。

查看 Qt 属性绑定和其他响应式语言的实现可能会给您一些实现思路。

Wikipedia页面还标识了用于 Javascript、.NET、Python、Java、C++ 和许多其他语言的反应式编程的库。

于 2013-07-18T14:54:46.890 回答
0

我认为您可以在这里使用多态性。有一个货币列表,每个货币都包含一个向量,其中包含所有依赖元素的指针(指向基类)。

基类强制它们包含一个函数update(),每次更新当前货币时都会调用该函数。

depedant 元素依次具有它们所依赖的每种货币的指针,并在其update()实现中使用这些指针来更新自己。

#include<iostream>
#include <vector>
class c_node_combi_base;
class currency
{
  std::vector<c_node_combi_base*> m_dependant;
  double m_val;
public:
  double value (void) const { return m_val; }
  void reg (c_node_combi_base * p) { m_dependant.push_back(p); }
  void update (double val);
};
class c_node_combi_base
{
  std::vector<currency*> currencies;
public:
  virtual void update (void) = 0;
};

template<size_t N, typename OP> // templated to differentiate types of nodes
class currency_node : public c_node_combi_base 
{ 
};

struct divide_d 
{
  double operator() (const double x, const double y) const {return x/y;}
};

template<typename OPT> // node type 2
class currency_node<2u, OPT> 
  : public c_node_combi_base
{
  currency *A, *B;
  OPT _op;
  double m_val;
public:
  currency_node (currency * a, currency * b)
    : A(a), B(b), _op(), m_val(_op(A->value(), B->value())) 
  {
    A->reg(this);
    B->reg(this);
  }
  void update (void) 
  { 
    m_val = _op(A->value(), B->value());
  }

  double value (void) { return m_val; }

};

void currency::update (double value) 
{
  m_val = value; 
  for (size_t i=0; i<m_dependant.size(); ++i)
  {
    m_dependant[i]->update();
  }
}

这使得:

int main (void)
{
  currency franc, dinar;
  franc.update(9.9);
  dinar.update(3.3);
  currency_node<2, divide_d> franc_dinar(&franc, &dinar);
  std::cout << franc_dinar.value() << std::endl;
  dinar.update(1.1); // updates franc_dinar automatically
  std::cout << franc_dinar.value() << std::endl;
}

印刷:

3
9


也许您可以拥有一种std::vector<std::weak_ptr>货币,而每个节点都std::shared_ptr为每种货币持有一种货币,因此除非没有更多节点引用它们,否则货币可能不会超出范围/被销毁

于 2013-07-18T15:10:46.040 回答