我正在寻求关于如何设计以下场景的建议:
我有一个三维向量类,条目是double类型的.如果两个向量的对应元素差小于给定的公差,例如10^(-10),我希望它们被认为是相等的。
(代码示例在C#语法中与伪代码混合使用。)
public class Vector
{
double x, y, z;
public bool TolEquals(Vector other)
{
return ((|x-other.x| < 10^(-10)) // absolute value of difference
&& (|y-other.y| < 10^(-10)) // must be small enough
&& (|z-other.z| < 10^(-10)));
}
}我需要一定的灵活性,因为其他人可能希望使用比10^(-10)更多的容忍度值。
这意味着,我需要将公差值以某种方式合并到我的等式检查方法中,例如:
public bool TolEquals(Vector other, double tolerance)
{
return ((|x-other.x| < tolerance)
&& (|y-other.y| < tolerance)
&& (|z-other.z| < tolerance);
}但我知道,在我的整个申请,容忍将是不变的。我不想写
v1.TolEquals(v2, tolerance)每次我检查是否相等,因为它不是很容易读,因为打字可以很容易发生。
另一个想法是将tolerance存储为Vector类的成员,并将其设置在Vector的构造函数中。那么TolEquals方法只需要一个参数:
public class Vector
{
double x, y, z;
double tolerance;
public Vector(double x, double y, double z, double tolerance)
{
this.x = x;
this.y = y;
this.z = z;
this.tolerance = tolerance;
}
public bool TolEquals(Vector other)
{
return ((|x-other.x| < tolerance)
&& (|y-other.y| < tolerance)
&& (|z-other.z| < tolerance));
}
}但是,使用我的代码的人可能会意外地创建具有不同公差的两个向量v和w,并从v.TolEquals(w)和w.TolEquals(v)那里得到不同的答案。这也不应该发生。
我可能迟早会向我的Vector类中添加一些静态方法,这些方法以几个向量作为输入,并与它们一起进行一些涉及TolEquals的计算。所以我需要确保所有的向量都使用相同的容差值。
我正在寻找一种解决方案,允许我在程序中的某个地方定义tolerance,在Vector类之外,然后所有向量都使用该tolerance作为他们的TolEquals。而且我希望能够在运行时更改tolerance (现在不需要它,但以后可能需要它),因此所有的向量都使用新的值。但可能是使用我的代码的人需要两个不同的容差值共存。他将有两组不同的载体,使用不同的容忍度值,但这两组不相互作用。
所以静态字段tolerance也是不可选的。
我还将编写一个matrix类,也许还会编写一些类似于TolEquals的类,因此解决方案不应该局限于我的Vector类。
有什么想法吗?
在接受答案的启发下,我想提出我的最终解决方案(在C#中),并发表一些评论。所有的信息都是在被接受的答案中给出的,但是我花了一些时间来处理它,所以我会在这里对它做一些改进。我决定在泛型向量类中使用静态字段来实现它:
public interface ISpace { double TOL(); }
public class ZeroTolSpace : ISpace { public double TOL() => 0; }
public class Tol10Space : ISpace { public double TOL() => 1E-10; }
// Others can easily write new classes for other tolerance values.
public class Vector<T> where T: ISpace, new()
{
private double x, y, z;
public static double? tol = null; // Changeable at runtime (if really needed)!
public Vector(double x, double y, double z)
{
this.x = x; this.y = y; this.z = z;
tol = tol ?? (new T()).TOL(); // Since tol is static, the new-operator
// gets called only once, minimzing overhead.
}
public bool TolEquals(Vector other)
{
return ((Math.Abs(x-other.x) < tol)
&& (Math.Abs(y-other.y) < tol)
&& (Math.Abs(z-other.z) < tol));
}
}然后在Main方法中我们可以这样做:
Vector<ZeroTolSpace> v1 = new Vector<ZeroTolSpace> (0, 0, 0);
Vector<ZeroTolSpace> v2 = new Vector<ZeroTolSpace>(1E-11, 1E-11, 1E-11);
v1.TolEquals(v2)); // false since _tol is 0
Vector<Tol10Space> w1 = new Vector<Tol10Space> (0, 0, 0);
Vector<Tol10Space> w2 = new Vector<Tol10Space> (1E-11, 1E-11, 1E-11);
w1.TolEquals(w2)); // true since tol is 1E-10
v1.TolEquals(w2)); // compile error: types do not match矩阵和其他依赖于公差的类可以以同样的方式实现。
发布于 2016-07-19 14:42:49
但我知道,在我的整个申请,容忍将是不变的。
这有点可疑,因为这种容忍通常是非常特定于算法的。我不得不面对很多痛苦,因为有人认为他们可以在任何地方使用持续的宽容来逃避痛苦。
但假设你真的想要这个,我可能会这样做:
public class Space {
public Space(double tolerance);
public Vector vector(double x, double y, double z);
public Matrix matrix(...);
}因此,您创建了一个Space类,它提供了创建向量的唯一方法。不能组合由不同空间类创建的向量。在特定的应用程序中,您只需创建一个空间常量,它定义了所使用的容忍度。
我希望能够在运行时改变容忍度(现在不需要它,但以后可能需要它),从而导致使用新值的所有向量。
不,你没有。在运行时改变容忍度会导致痛苦和痛苦。如果您的代码需要在不同的情况下具有不同的公差,那么您确实希望传递一个参数。
现在,真的,如果我们可以使用泛型,那就更好了:
Vector<MySpace>然后在MySpace上有一个静态方法定义公差。然后,类型系统可以验证您从来没有混合不同类型的向量。唉,这超出了某些编程语言的范围,所以您可能会也可能无法使用它。
==编辑==
C++可以通过模板很好地完成这一任务:
struct MySpace {
public static double tolerance = 0.000001;
}
template<typename T>
class Vector {
Vector(double x, double y, double z);
// We can refer to T::tolerance
}
// I can use Vector<MySpace> to refer to vectors with a particular
// space, and have the type system enforce that.但这取决于这样一个事实,即C++模板可以自由引用其类型参数中的元素。但其他语言往往缺乏这种能力。
在C#中,您可以做类似的事情(我不做C#,但我相信这是有效的)
interface Space {
double tolerance();
}
public class MySpace extends Space {
double tolerance() {
return 0.000001;
}
}
public class Vector<T> where T: Space, new() {
public Vector(double x, double y, double z);
// I can get my tolerance via (new T()).tolerance()
// We hope that the optimizer can get rid of the overhead.
}但是在Java中,这是行不通的,因为我们不能做new T。我们可以这样做:
class Vector<T extends Space> {
Vector(T t, double x, double y, double z);
// t.tolerance() will be the tolerance.
}然后按照惯例,始终通过单例子类创建Space的实例。
===
关于Java解决方案的更多细节
在图书馆:
public interface Space {
double tolerance();
public static <T extends Space> double toleranceForSpace(Class<T> spaceClass) {
return spaceClass.newInstance().tolerance();
}
}
public Vector<SpaceType> {
Vector(Class<SpaceType> spaceType, double x, double y, double z);
// call Space.toleranceFor(spaceType) to get the tolerance
}在非图书馆代码中:
class MySpace extends Space {
double tolerance() { return 0.1; }
}
Vector<MySpace> space = new Vector<>(MySpace.class, 2.3, 4.5, 6.7);发布于 2016-07-19 20:24:44
在这里,可选参数可以很好地工作。
public class Vector {
public static readonly double DefaultTolerance = 0.0001; // or whatever.
public bool Equals(Vector other, double? tolerance = null) {
tolerance = tolerance ?? DefaultTolerance;
// go about your business
}
}
// later
w.Equals(v);
w.Equals(v, 0.001);由于公差适用于运算,而不是数据,所以向量本身不拥有它是很好的。
https://softwareengineering.stackexchange.com/questions/325213
复制相似问题