这是我经常遇到的某种软件工程和语言设计问题,因为我没有任何语言的良好解决方案。我对C ++解决方案最感兴趣,但其他(希望有词汇范围)语言的解决方案也值得考虑。
这是一个例子。让我们说我有一些代码,就像这样:
template<class T, class F>
T foo(T a, T b, T c, T d, F func) { return func() / (a * d - b * c); }
我认为调用者应该能够使用foo
进行模运算以及常规算术。
换句话说,对于finite_field
的适当定义,在理想世界中,应该在有限域而不是在实数域中评估上面的代码: / p>
int main(int argc, char *argv[])
{
finite_field<int> scoped_field(argc /* let's say this is my modulus */);
return foo(1, 2, 3, 4, []{ return +1; }) + foo(4, 3, 2, 1, []{ return -1; });
}
然而,很明显,这不起作用,因为foo
内部没有任何内容(具体而言,没有任何操作符)知道scoped_field
强加的算术上下文。
所有我所知道的解决方案非常难看:
完全停止使用算术运算符
使用add(x, y)
代替x + y
,div(x, y)
代替x / y
,等等
然后可以将所有这些放在某种类的Arithmetic
类中,并使用this
来访问当前的&#34;算术上下文&#34;。
优点:它有效,并且不需要存储多余的数据。
缺点:需要编辑foo
,这可能不是必需的,同时使其不那么令人愉悦,也更难以阅读和写作。
定义包装ModInt
的自定义int
类型,将模数存储在每个数字中,并重载该类型的运算符以从中读取模数其中一个输入参数。
优点:它有效,并且不需要修改foo
的正文。
缺点:低效且容易出错 - 每个模数存储在每个整数内,这意味着在运行时可能存在冲突错误,以及明显的O(n)空间低效率。更不用说评估上下文不是数字的属性,而是运营商本身的属性。
存储&#34;当前上下文&#34;在一个线程局部变量中,并根据上下文重载操作符以表现不同。
优点:它有效(有点)。它不会浪费空间或需要修改foo
。
缺点:丑陋,不太便携,不可重复或容易出错,具体取决于它是如何实现的(它会污染被调查者和算术运算符上下文)
所以,我实际上并不知道任何可读,可移植,和可维护的解决方案。
据我所知,似乎我根本不得不放弃其中一个。
这是一个常见或众所周知的问题吗?
它是否在任何相当流行的语言中都有一个优雅的解决方案?如果是,那些,以及如何?
可以用C ++专门解决吗?是否有某种设计模式或成语?
答案 0 :(得分:0)
尽管如此,foo
的一个小修改可能会产生预期的结果。
template<typename T, typename F, typename F2>
T foo(T a, T b, T c, T d, F func, F2 mod) { return func() / (mod(a) * d - mod(b) * c); }
int main(int argc, char *argv[])
{
auto mod = [](int i) { return ModInt(i, modulus); };
return foo(1, 2, 3, 4, []{ return +1; }, mod) + foo(4, 3, 2, 1, []{ return -1; }, mod);
}
由于mod
的每次使用都使用相同的本地,因此ModInt
无法使用不同的模数。并且由于mod
仅在运算符优先级时被调用,并且需要它并且任何本地人都被销毁并且它们的存储被重新使用,所以我们就是金色的w.r.t.储存空间。 (另外,严肃地说,堆栈上有几个整数?没什么大不了的。)
但从本质上讲,模运算是所涉及的运算符的属性。这就是它的定义方式和工作原理。你可以破解它,但无论你做什么,它至少会吮吸一点。您需要一个用户定义的运算符,但是您不能隐式地为两个基本类型传递用户定义的函数,而且您也不能。
答案 1 :(得分:0)
希望我能正确理解你的问题。简而言之,我猜你想在运行时决定一组重载算术运算的行为。这样的事情会起作用吗?
struct Arithmetic;
struct MyInt {
int value;
MyInt operator+(const MyInt& other);
static void StandardArithmetic();
static void ModularArithmetic();
static Arithmetic* arithmetic;
};
struct Arithmetic { virtual MyInt Add( MyInt, MyInt ) const = 0; };
struct StandardArithmetic : public Arithmetic {
virtual MyInt Add( MyInt a, MyInt b) const {
MyInt result = {a.value + b.value};
return result;
}
};
struct ModularArithmetic : public Arithmetic { /* ... */ };
在.cpp
:
Arithmetic * MyInt::Arithmetic = 0;
void MyInt StandardArithmetic() {
delete arithmetic;
arithmetic = new StandardArithmetic;
}
/* ... */
MyInt MyInt::operator+(const MyInt& other) const {
return arithmetic->add(*this, other);
}
使用此功能,您可以通过调用MyInt::Arithmetic
轻松切换不同类型的算术。您也可以将算术指针存储在例如抽象基类的静态成员,如果要实现各种算术运算,这将是有意义的。