是否有一种自然的方法来定义0到1之间的数字类型?基本上一个人可以用double做一切,问题是我应该在一些函数中定义一些绑定检查,并且我有(可能是愚蠢的)想法将绑定检查外包给一个类,比如
class Probability {
// the value of the Probability
double val;
Probability(double val):value(val){
// freak out if val > 1 or val < 0
//...
//
};
// operators such as
Probability operator + (Probability const & a, Probability const & b){
double result a.val + b.val;
if ((result > 1) || (result < 0)){
// freak out
result = 0
}
return result;
}
// ...
//
}
这种方法的问题可能是它减慢了每个操作的速度。有更快的绑定检查方式吗?我也想知道如何处理上面代码中的“怪异”部分。
答案 0 :(得分:2)
您可以使用类Probability
强制执行边界,在内部存储double。如果希望P(0.75) + P(0.5) - P(0.6)
之类的操作起作用,则可以让操作符返回一个代理对象,该对象不进行边界检查。此代理对象将具有转换运算符Probability
,Probability
构造函数将检查边界。如果您只是直接使用Probability
类型,并允许ProbabilityResultProxy
形式的临时表,那么您将获得所需的行为。
下面的示例概述了这种方法。显然,在实际实现中你需要很多缺失,但我想专注于一个特定的解决方案,而不是提供一个完整的类。
#include <iostream>
class Probability {
public:
Probability(double value) {
if (value < 0 || value > 1) throw std::runtime_error("Invalid probability");
value_ = value;
}
double value() const { return value_; }
private:
double value_;
};
class ProbabilityResultProxy {
public:
explicit ProbabilityResultProxy(double p) : value_(p) {}
double value() const { return value_; }
operator Probability() {
return Probability(value_);
}
private:
double value_;
};
ProbabilityResultProxy operator+(const Probability& lhs, const Probability& rhs) {
return ProbabilityResultProxy(lhs.value() + rhs.value());
}
ProbabilityResultProxy operator+(const ProbabilityResultProxy& lhs, const Probability& rhs) {
return ProbabilityResultProxy(lhs.value() + rhs.value());
}
ProbabilityResultProxy operator+(const Probability& lhs, const ProbabilityResultProxy& rhs) {
return ProbabilityResultProxy(lhs.value() + rhs.value());
}
ProbabilityResultProxy operator+(const ProbabilityResultProxy& lhs, const ProbabilityResultProxy& rhs) {
return ProbabilityResultProxy(lhs.value() + rhs.value());
}
ProbabilityResultProxy operator-(const Probability& lhs, const Probability& rhs) {
return ProbabilityResultProxy(lhs.value() - rhs.value());
}
ProbabilityResultProxy operator-(const ProbabilityResultProxy& lhs, const Probability& rhs) {
return ProbabilityResultProxy(lhs.value() - rhs.value());
}
ProbabilityResultProxy operator-(const Probability& lhs, const ProbabilityResultProxy& rhs) {
return ProbabilityResultProxy(lhs.value() - rhs.value());
}
ProbabilityResultProxy operator-(const ProbabilityResultProxy& lhs, const ProbabilityResultProxy& rhs) {
return ProbabilityResultProxy(lhs.value() - rhs.value());
}
int main() {
Probability p1(0.75);
Probability p2(0.5);
Probability p3(0.6);
Probability result = p1 + p2 - p3;
std::cout << result.value() << "\n";
try {
Probability result2 = p1 + p2;
std::cout << result2.value();
} catch (const std::runtime_error& e) {
std::cout << e.what() << "\n";
}
return 0;
}
此处,为Probability
和ProbabilityResultProxy
的每个组合定义了数学运算符。每个操作都返回一个代理对象,最后的赋值会导致执行边界检查。
如果您愿意,可以将ProbabilityResultProxy
设为Probability
的私有成员,并使Probability
的运营商成为朋友。这可以防止任何人直接实例化代理类。
答案 1 :(得分:1)
“怪胎”选项很简单:throw std::invalid_argument
。投掷有点慢是没关系的;你无论如何都没有得到答案。好处是,抛出不是很快,因为优化器可以假设非异常路径更有可能。
在性能方面,拥有IntermediateResult
类可能是有益的,这样只会对返回Probability
的最终作业进行范围检查。这会回避0.75+0.5-0.6
示例。