Javascript:
function bounds(a, b, c) {
null != b && (a = Math.max(a, b));
null != c && (a = Math.min(a, c));
return a
}
Run Code Online (Sandbox Code Playgroud)
老实说,我什至不知道这里发生了什么。如果b
不为null,则a
设置set Math.max(a, b)
。如果c
不为null,a
则从设置Math.min(a, c)
?
如何将其转换为C#?
PS:这是我的翻译。它是否正确?
private double bounds(double a, double? b, double? c) {
if (b != null) a = Math.Max(a, b.Value);
if (c != null) a = Math.Min(a, c.Value);
return a;
}
Run Code Online (Sandbox Code Playgroud)
Eri*_*ert 10
老实说,我什至不知道这里发生了什么。
我也不。该代码出现执行合同“,如果c
是的最小元素,返回它,否则,返回的中间元素a
,b
和c
,再加上一些空性未知目的的检查。
这是一个离奇的合同,使我认为这可能是未经测试的“返回中间元素”的实现,其中存在错误。如果c
实际上从来不是最小的元素,那么也许永远不会发现该错误。
如何将其转换为C#?
了解上下文中代码的含义,然后在惯用的C#中实现这些语义。重要的是理解。一旦理解了代码,编写正确,可测试的实现就会很简单。
这是我的翻译。它是否正确?
private double bounds(double a, double? b, double? c) {
if (b != null) a = Math.Max(a, b.Value);
if (c != null) a = Math.Min(a, c.Value);
return a;
}
Run Code Online (Sandbox Code Playgroud)
正确的是,它可以准确地实现原始的,可能是错误的代码的语义。但这并没有更好。让我们改善它。突出三点:
this
,所以应该消耗static
。a
,b
,c
,有含义给他们难以阅读,因为它们掩盖那些含义。让我们修复所有这些。
private static double Bounds(double a, double? b, double? c)
{
double larger = (b == null) ? a : Math.Max(a, b.Value);
double smaller = (c == null) ? larger : Math.Min(larger, c.Value);
return smaller;
}
Run Code Online (Sandbox Code Playgroud)
我们仍然不具备良好的名称a
,b
和c
,但至少我们知道,larger
和smaller
反映它们的含义。
我们完了吗?总是问自己,我该如何简化?
您可以通过将支票移到帮助程序中来简化操作。
private static double Max(double x, double? y) =>
(y == null) ? x : Math.Max(x, y.Value)
private static double Min(double x, double? y) =>
(y == null) ? x : Math.Min(x, y.Value)
Run Code Online (Sandbox Code Playgroud)
现在我们的方法变为:
private static double Bounds(double a, double? b, double? c)
{
double larger = Max(a, b);
double smaller = Min(larger, c);
return smaller;
}
Run Code Online (Sandbox Code Playgroud)
现在我们意识到我们可以将其重写为
private static double Bounds(double a, double? b, double? c) =>
Min(Max(a, b), c);
Run Code Online (Sandbox Code Playgroud)
当然,目前还不清楚这件事的目的是什么,但我希望你同意我的执行行动尚不明确。在我的实现中,语义显然是“如果c
至少是,则返回它,否则返回中间元素”。将我的版本的可理解性与原始的混乱情况进行比较,看看哪一个更容易理解。
这是什么教训?
进一步的沉思;假设我的猜想是正确的,并且打算写:
private static double Middle(double test, double? bottom, double? top)
Run Code Online (Sandbox Code Playgroud)
该方法的语义是:
bottom
并且top
都可以为null。bottom
必须较小。bottom
和top
都不都是null,则返回中间的值。bottom
和top
均为null,则test
中间的值为。bottom
为null但top
不为null,则test
and中的较小者top
为中间的值。在这里,我们将“ null”(像“负无穷大”)当作底部。top
为null但bottom
不为null ,则bottom
and中的较大者test
为中间的那个。在这里,空顶被视为“正无穷大”。这实际上是一个非常明智的合同,但是它要求我们知道它bottom
总是比top
它们都不均为null时小。在C#中Debug.Assert
,良好的实现可能是事实(如果方法为private
),或者throw
前提条件被违反(如果方法为public
)。
如果是这种情况,那么更好的选择是这样做:
private static double Middle(double test, double? bottom, double? top) =>
Middle(
test,
bottom ?? Double.NegativeInfinity,
top ?? Double.PositiveInfinity);
private static double Middle(double test, double bottom, double top) =>
Math.Min(Math.Max(test, bottom), top);
Run Code Online (Sandbox Code Playgroud)
(如果您不熟悉??
,则将其读为“ coalesce”,x??y
意思是“如果x
不为null,则使用x.Value
,否则使用y
”。这是一个非常有用的运算符。)
再次,看一下代码的可读性,看它看起来多么漂亮和容易。很明显,“ null表示底部为负无穷大,顶部为正无穷大”,因为这就是代码所说的!
或者,编写不需要对三个输入进行任何排序的Middle的正确实现:
// True if test is between b1 and b2, false otherwise
private static bool Between(double test, double b1, double b2) =>
((b1 <= test) & (test <= b2)) | ((b2 <= test) & (test <= b1));
// Returns the middle value of a, b, and c.
private static double Middle(double a, double b, double c)
{
if (Between(a, b, c) return a;
if (Between(b, a, c) return b;
return c;
}
Run Code Online (Sandbox Code Playgroud)
或者,如果您喜欢
private static double Middle(double a, double b, double c) =>
Between(a, b, c) ? a : Between(b, a, c) ? b : c;
Run Code Online (Sandbox Code Playgroud)
现在,我们可以再次轻松编写Bounds方法:
private static double Bounds(double test, double? bottom, double? top) =>
Middle(
test,
bottom ?? Double.NegativeInfinity,
top ?? Double.PositiveInfinity);
Run Code Online (Sandbox Code Playgroud)
再次考虑一下我们在这里追求的目标。简单的方法可以做一件事情,并且做得非常好。一旦有了这些,就可以使用这些方法来构建其他简单的方法,并逐渐增加程序的复杂性而又不会让人难以理解。