2011-02-27 34 views
1

我工作的DSEL并想有以下几点:C++表达式作为变量

Bra ij; 
Ket kl, cd; 

(ij||kl); // initialize some array 
(ij||cd); // ditto 

.... 
T(i,j,k,l)*(ij||kl); // do some math without recomputing (ij||kl) 

所以基本上我想有表达作为变量。可能吗?

到目前为止,我的想法是有一个“单身”工厂,使用表达式(ij|kl)生成/查找阵列。还要别的吗?

回答

2

如果您不想重新计算ij||kl,那么只需将它们存储到表达式返回的任何类型的变量中。这正是变量存在的原因之一。


好的,这是我能想到做这件事的唯一方法,虽然听起来不太漂亮。你可以做的是,当运营商||函数被调用,将操作数和结果存储到实例变量中(如果operator ||是某个类的成员函数),或者存储到一个静态分配的变量中(如果operator ||声明为寂寞)。

下一次操作符||被调用,检查操作数是否与最后一次调用相同。如果是,只需返回您存储的最后结果。否则,计算一个新的结果。

这应该做的伎俩。讨厌的部分是,它需要将操作数复制到其他变量,这可能会因环境而变得昂贵。不管怎样,如果变量是不变的,你可以保留指向操作数的指针,这会比较便宜一些。

如果你想更进一步,你可以使用map或其他东西来存储操作数和多个先前调用的结果。这样,这将工作,如果你需要为几个不同的计算做到这一点。

+0

我想尽量保持记法尽可能接近论文 – Anycorn 2011-02-27 04:22:02

+0

是的,这也是我最终想到的。丑陋的后端比丑陋的前端更好。 – Anycorn 2011-02-27 04:42:44

0

这是编译器的工作,以决定表达式是否需要重新评估或可以从以前的评估中重新使用。让编译器完成它的工作。

你应该专注于尽可能简明清楚地表达自己(不管你是否使用变量完全依赖于你和上下文)。但是如果表达式使用未改变的对象(即未使用非成本方法分配或修改的对象),那么编译器可能会优化并重新计算相同的表达式。

注意。为了帮助编译器确保将方法正确标记为const。

+0

生成的数组是N^5复杂性 - 我当然不希望编译器在任何情况下重新评估它。 – Anycorn 2011-02-27 04:34:06

+0

@aaa:然后让它保持不变。它永远不会被重新评估。 – 2011-02-27 04:48:11

2

要做这种事情,很明显你需要某种全球性的知识。换句话说,您将不得不构建某种全局或半全局的结构来保存操作/表达式的记录。

我建议你在节点是表达式(变量是一个零元运算符的特例)中构造一个图形(可能带有BGL)。如果将每个操作数作为入射顶点连接到运算符的顶点,则可以构造一个图形。之后,当实际评估表达式时,您可以先用一些修剪规则遍历图形,以消除冗余操作。如果你确定图中的顶点都不重复,那么修剪是隐含的,我猜想。

如果你想避免使用全局数据,我可以建议你使用某种类型的表达式管理器类并注册所有的操作。例如,像这样:

int main() { 
    expression_handler expression; //have some class to handle a sequence of operations. 

    expression 
    << (ij||kl) 
    << (ij||cd) 
    << .... 
    << T(i,j,k,l)*(ij||kl); //register all the expressions, in order. 

    expression.evaluate(); //evaluate the expression (possibly optimizing the graph before) 

    return 0; 
}; 
+0

这是绝对有趣的方法。提升proto,我会首先想到,而不是BGL – Anycorn 2011-02-27 05:52:00

+0

Boost Proto肯定是一种选择。然而,我不确定使用它执行(半)全局查找表达式是多么容易。我猜想BGL的功能比较丰富(当然,它在GP中有更多GP的味道,而不是Proto中的TMP)。这取决于您所需的编译时和运行时机制的级别。 – 2011-02-27 06:55:08

2

我会把它作为Ken和Mikael的建议的组合。操作符重载时这种东西很容易,特别是当你在每边都有自定义类型时。

  1. the Bra :: operator ||方法返回一个临时对象ExpressionHandle。

  2. 当创建ExpressionHandle,它也有一个全球性的商店注册ExpressionBody 内部(这可以让你避免使用实例 变量,但意味着你必须保留所有的表情,直到 程序或结束一个明确的发布)。

  3. ExpressionBody对象既是一个声明(表达式树),也是一个可选结果。您可以使用懒惰技术来仅在最终调用它们时对它们进行评估。

  4. 调用一个ExpressionBody当由线等

    T(I,J,K,L)创建的ExpressionHandle发生*(IJ || KL);

  5. 上面的(ij || kl)会创建一个ExpressionBody并试图注册它,但是找到一个现有的全局对象,所以只需返回一个指向全局实例的ExpressionHandle即可。

  6. operator *(const ExpressionHandle &,blah)会要求ExpressionBody返回结果,此时它会返回缓存结果或执行第一次评估并对其进行缓存。