我们以二次方程为例:
a x^2 + b x + c = 0
可以将此等式视为描述值 a、b、c 和 x 之间的关系。给定其中三个,你可以计算出第四个。四种可能性是:
a = - (b x + c) / x^2
b = - (a x^2 + c) / x
c = - x (a x + b)
x = [-b +- sqrt(b^2 - 4 a c)] / (2 a)
这是表示该等式的一种方法。给定以下类(class):
class Quadratic
{
public:
double a; bool HasA = false; void A(double a_) { a = a_; HasA = true; }
double b; bool HasB = false; void B(double b_) { b = b_; HasB = true; }
double c; bool HasC = false; void C(double c_) { c = c_; HasC = true; }
double x; bool HasX = false; void X(double x_) { x = x_; HasX = true; }
// a = - (b x + c) / x^2
double A()
{
if (HasB == false) throw domain_error("B not set");
if (HasC == false) throw domain_error("C not set");
if (HasX == false) throw domain_error("X not set");
if (x == 0.0) throw domain_error("X cannot be 0.0");
return - (b*x + c) / (x*x);
}
// x = [-b +- sqrt(b^2 - 4 a c)] / (2 a)
vector<double> X()
{
if (HasA == false) throw domain_error("A not set");
if (HasB == false) throw domain_error("B not set");
if (HasC == false) throw domain_error("C not set");
if (a == 0.0) throw domain_error("A cannot be 0.0");
return
{
(-b + sqrt(b*b - 4 * a*c)) / (2 * a),
(-b - sqrt(b*b - 4 * a*c)) / (2 * a)
};
}
// b = - (a x^2 + c) / x
// ...
// c = - x (a x + b)
// ...
};
我们可以按如下方式找到x
。设置A
、B
和C
:
obj.A(2.3);
obj.B(3.4);
obj.C(1.2);
X
可能有两个值,因此遍历结果:
for each (auto elt in obj.X()) cout << elt << endl;
如果未设置任何依赖值,则会抛出 domain_error
异常。
同样,为了找到A
,我们设置B
、C
和X
:
obj.B(1.2);
obj.C(2.3);
obj.X(3.4);
并显示结果:
cout << obj.A() << endl;
我的问题是,是否有其他方法可以用面向对象的语言表示和处理方程式?是否有比上述方法更惯用的方法?
最佳答案
你的问题的标题是这样的:
Object-oriented API for equations
但是,您的代码示例没有任何面向对象的内容,至少在我所知道的“面向对象编程”的既定定义中没有。 你没有虚函数,所以它不是面向对象的。
Bjarne Stroustrup 的常见问题 "What is "OOP" and what's so great about it?"说(重点是我加的):
In the context of C++ [...] it means programming using class hierarchies and virtual functions to allow manipulation of objects of a variety of types through well-defined interfaces and to allow a program to be extended incrementally through derivation.
标准 C++ 常见问题解答(也引用了第一个来源),答案 "Are virtual functions (dynamic binding) central to OO/C++?"像这样:
Without virtual functions, C++ wouldn’t be object-oriented.
因此,
My question is, are there other approaches to representing and working with equations in an object-oriented language?
答案应该是数学计算和面向对象编程通常不能很好地结合。面向对象就是在运行时选择抽象操作的具体实现。例如,您可以根据用户在运行时 的选择选择具有相同输入和输出的不同算法。这可以通过虚函数来完成。不过,面向对象会发生在您的应用程序的更高级别,并且计算本身不会是面向对象的。
Is there a more idiomatic approach than the above?
是的,通用编程,即模板。
您提供的所有代码都适用于 double
值。如果我想将它与 float
一起使用怎么办? , std::complex<double>
甚至是自定义 BigNumber
类(class)?
使用模板,您可以编写通用代码,并在编译时选择具体实现。
首先,让我们让您的原始代码可编译:
#include <vector>
#include <stdexcept>
#include <math.h>
class Equation
{
public:
bool HasA;
bool HasB;
bool HasC;
bool HasX;
double a;
double b;
double c;
double x;
double A()
{
if (!HasB) throw std::domain_error("B not set");
if (!HasC) throw std::domain_error("C not set");
if (!HasX) throw std::domain_error("X not set");
if (x == 0.0) throw std::domain_error("X cannot be 0.0");
return - (b*x + c) / (x*x);
}
// x = [-b +- sqrt(b^2 - 4 a c)] / (2 a)
std::vector<double> X()
{
if (!HasA) throw std::domain_error("A not set");
if (!HasB) throw std::domain_error("B not set");
if (!HasC) throw std::domain_error("C not set");
if (a == 0.0) throw std::domain_error("A cannot be 0.0");
return
{
(-b + sqrt(b*b - 4 * a*c)) / (2 * a),
(-b - sqrt(b*b - 4 * a*c)) / (2 * a)
};
}
// b = - (a x^2 + c) / x
// ...
// c = - x (a x + b)
// ...
};
int main()
{
Equation e;
std::vector<double> v = e.X();
}
(我已经修复了 == false
比较,这几乎总是糟糕的风格,但是从 C++ 编码质量 POV 来看还有更多工作要做,例如,将成员变量设为私有(private)。)
问题是整个事情只适用于 double
秒。如果您尝试将它与 int
一起使用s,这是发生了什么:
int main()
{
Equation e;
std::vector<int> v = e.X();
}
结果:
error C2440: 'initializing' : cannot convert from
'std::vector<double,std::allocator<_Ty>>' to 'std::vector<int,std::allocator<_Ty>>'
以下是将类转换为模板的方法:添加 template <class T>
在顶部并替换每个 double
与 T
(并添加两个 static_cast
告诉编译器您同意缩小转换,这可能由于 sqrt
的返回类型而发生):
#include <vector>
#include <stdexcept>
#include <math.h>
template <class T>
class Equation
{
public:
bool HasA;
bool HasB;
bool HasC;
bool HasX;
T a;
T b;
T c;
T x;
T A()
{
if (!HasB) throw std::domain_error("B not set");
if (!HasC) throw std::domain_error("C not set");
if (!HasX) throw std::domain_error("X not set");
if (x == 0.0) throw std::domain_error("X cannot be 0.0");
return - (b*x + c) / (x*x);
}
// x = [-b +- sqrt(b^2 - 4 a c)] / (2 a)
std::vector<T> X()
{
if (!HasA) throw std::domain_error("A not set");
if (!HasB) throw std::domain_error("B not set");
if (!HasC) throw std::domain_error("C not set");
if (a == 0.0) throw std::domain_error("A cannot be 0.0");
return
{
static_cast<T>((-b + sqrt(b*b - 4 * a*c)) / (2 * a)),
static_cast<T>((-b - sqrt(b*b - 4 * a*c)) / (2 * a))
};
}
// b = - (a x^2 + c) / x
// ...
// c = - x (a x + b)
// ...
};
int main()
{
Equation<int> e;
std::vector<int> v = e.X();
}
当然,这只是故事的一半,因为很有可能您确实不想允许整数类型,只允许浮点类型,例如double
。或 float
(或自定义浮点类型)。 sqrt(2)
的结果截断为 1
很少可取。
为了让您的代码保持通用但防止出现此类问题,请继续阅读 static assertions用于编译时检查,将模板限制为特定类型。 std::is_floating_point
也可能有用。另请参阅以下最近关于 SO 的问题:
Getting std::complex<double> to pass std::is_floating_point test
请记住,这与面向对象编程没有任何关系。
关于c++ - 面向对象的方程式 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25706765/