7

是否有普遍接受的最佳方法来编写复杂的数学?例如:

double someNumber = .123 + .456 * Math.Pow(Math.E, .789 * Math.Pow((homeIndex + .22), .012));

这是否可以对数字进行硬编码?还是每个数字都应该有一个与之相关的常数?或者还有其他方法,比如将计算存储在配置中并以某种方式调用它们?

会有很多这样的代码,我正在努力保持它的可维护性。

注意:上面显示的示例只是一行。会有数十或数百行这样的代码。不仅数字会改变,公式也会改变。

4

8 回答 8

19

一般来说,有两种常量——一种对实现有意义,一种对业务逻辑有意义。

对第一类常量进行硬编码是可以的:它们对于理解你的算法是私有的。例如,如果您正在使用三元搜索并且需要将区间分成三部分,那么用硬编码进行划分3是正确的方法。

另一方面,在您的程序代码之外具有含义的常量不应该被硬编码:给它们明确的名称可以让您离开公司后维护您的代码的人有非零的机会进行正确的修改而无需重写从头开始的事情或通过电子邮件向您寻求帮助。

于 2013-09-10T18:06:17.690 回答
4

“没事吧”?当然。据我所知,没有准军事警察部队围捕那些违背编程这一真正信仰的人。(然而。)。

明智吗?

嗯,有各种各样的方法来决定——性能、可伸缩性、可扩展性、可维护性等。

在可维护性范围内,这纯粹是邪恶的。它使可扩展性非常困难;性能和可扩展性可能不是一个大问题。

如果您留下了一个包含大量与上述类似的行的方法,您的继任者将没有机会维护代码。他建议重写是对的。

如果你把它分解成

public float calculateTax(person)
  float taxFreeAmount = calcTaxFreeAmount(person)
  float taxableAmount = calcTaxableAmount(person, taxFreeAmount)
  float taxAmount = calcTaxAmount(person, taxableAmount)
  return taxAmount
end

每个内部方法都有几行长,但是您在其中留下了一些硬编码的值-嗯,不是很出色,但也不是很糟糕。

但是,如果其中一些硬编码值可能会随着时间而变化(例如税率),那么将它们保留为硬编码值是不行的。太可怕了。

我能给的最好建议是:

  • 在 Resharper 度过一个下午,并使用它的自动重构工具。
  • 假设从你那里接这个的人是一个知道你住在哪里的挥舞斧头的疯子。
于 2013-09-10T18:43:27.797 回答
2

我通常会问自己是否可以在编写代码六个月后在凌晨 3 点睡眠不足时维护和修复代码。它对我很有帮助。看你的公式,我不确定我能不能。

很久以前,我在保险业工作。我的一些同事的任务是将精算公式转换为代码,首先是 FORTRAN,然后是 C。数学和编程技能因同事而异。我学到的是以下审查他们的代码:

  • 用代码记录实际的公式;没有它,多年后你将很难记住实际的公式。外部文档丢失、过时或根本无法访问。
  • 将公式分解为可以记录、重用和测试的离散组件。
  • 使用常数来记录方程;数几乎没有上下文,通常需要其他开发人员了解现有知识。
  • 尽可能依靠编译器来优化代码。一个好的编译器将内联方法,减少重复并优化特定架构的代码。在某些情况下,它可能会复制公式的某些部分以获得更好的性能。

也就是说,有时硬编码只是简化了事情,特别是如果在特定上下文中很好地理解了这些值。例如,将某个值除以(或乘以)100 或 1000,因为您要将值转换为美元。另一种方法是将小时数转换为秒数时乘以 3600。它们的含义通常是从更大的上下文中暗示出来的。以下内容对幻数 100 并没有多说:

public static double a(double b, double c)
{
     return (b - c) * 100;
} 

但以下内容可能会给您更好的提示:

public static double calculateAmountInCents(double amountDue, double amountPaid)
{
     return (amountDue - amountPaid) * 100;
}
于 2013-09-10T18:56:10.047 回答
1

正如上述评论所述,这远非复杂。

但是,您可以将数存储在 constants/app.config 值中,以便下一个开发人员更容易维护您的代码。

存储此类常量时,请务必向下一位开发人员解释(在 1 个月内阅读自己)您的想法是什么,以及他们需要记住什么。

也 ewxplain 实际计算的用途和用途。

于 2013-09-10T18:00:35.030 回答
1

不要像这样排队。

常量,因此当有人第一次查看您的代码时,您可以重用、轻松查找、轻松更改并提供更好的维护。

如果可以/应该自定义,您可以进行配置。客户改变价值有什么影响?有时最好不要给他们这个选择。他们可以自己改变它,然后当事情不起作用时责怪你。再说一次,也许他们比你的发布时间表更频繁地改变它。

于 2013-09-10T18:01:32.363 回答
0

值得注意的是,C# 编译器(或者是 CLR)将自动内联 1 行方法,因此如果您可以将某些公式提取到一个内衬中,您可以将它们提取为方法而不会造成任何性能损失。

编辑:

常量等或多或少取决于团队和使用量。显然,如果您多次使用相同的硬编码数字,请保持不变。但是,如果您正在编写一个可能只有您会编辑的公式(小团队),那么对值进行硬编码就可以了。这完全取决于您的团队对文档和维护的看法。

于 2013-09-10T18:03:24.240 回答
0

如果您的行中的计算为下一个开发人员解释了某些内容,那么您可以保留它,否则最好在您的代码或配置文件中计算常量值。

我在生产代码中发现了一行,就像:

int interval = 1 * 60 * 60 * 1000; 

没有任何评论,原始开发人员的意思1 hour是毫秒并不难,而不是看到3600000.

IMO 可能会省略计算,这样的情况会更好。

于 2013-09-10T18:09:09.250 回答
0

可以出于文档目的添加名称。所需文件的数量很大程度上取决于目的。

考虑以下代码:

 float e = m * 8.98755179e16;

并将其与以下内容进行对比:

 const float c = 299792458;
 float e = m * c * c;

即使变量名称在后者中不是很“描述性”,您也会更好地了解第一个代码在做什么 - 可以说不需要重命名cto speedOfLightmtomasseto 能量,因为名称是解释性的在他们的领域。

 const float speedOfLight = 299792458;
 float energy = mass * speedOfLight * speedOfLight;

我认为第二个代码是最清晰的——尤其是如果程序员可以期望在代码中找到 STR(LHC 模拟器或类似的东西)。总而言之 - 你需要找到一个最佳点。代码越冗长,您提供的上下文就越多 - 这可能有助于理解含义(什么是ec我们用质量和光速做某事)并模糊全局(我们将 c 平方并乘以 m 与需要扫描整行得到方程)。

大多数常数都有一些更深层次的含义和/或既定的符号,所以我会考虑至少按约定命名它(c对于光速,R对于气体常数,sPerH对于一小时的秒数)。如果符号不清楚,则应使用较长的名称(sPerH在名为的类中,Date或者Time在不在类中时可能很好Paginator)。真正明显的常数可以被硬编码(比如说 - 在合并排序中计算新数组长度时除以 2)。

于 2013-09-10T20:51:27.373 回答