我遇到了 C# switch 表达式的一个非常奇怪的行为,我不知道如何解释。所以,我正在练习解决一般面试问题,今天是
Kth Largest Element in an Array
。我在帖子的末尾发布了整个函数,但与问题相关的是函数的最后 5 行,我必须决定是否递归处理数组的左侧或右侧:
return hi - (k - 1) switch
{
> 0 => FindKthLargest(nums[..hi], k),
< 0 => FindKthLargest(nums[(hi + 1)..], k - hi - 1),
_ => nums[hi]
};
我认为它应该做什么:如果
hi
大于 k - 1
那么它递归地与左侧,如果它较小 - 右侧,如果它相等,那么它返回具有该索引的元素。它看起来非常简单,但并没有按预期工作。它不仅不起作用,而且会产生非常奇怪的结果。例如,对于输入 nums = [3, 2, 1, 5, 6, 4]
、k = 2
,该函数在我的本地计算机上返回 8
,这甚至不在数组中(并且您可以验证代码仅返回数组元素)。
无论如何,在摆弄它之后,我发现这个稍微修改过的代码确实有效:
var wtf = hi - (k - 1);
return wtf switch
{
> 0 => FindKthLargest(nums[..hi], k),
< 0 => FindKthLargest(nums[(hi + 1)..], k - hi - 1),
_ => nums[hi]
};
完全相同,但条件被移至单独的变量中。那么那里发生了什么?
函数全文:
public int FindKthLargest(Span<int> nums, int k)
{
if (nums.Length == 1)
return nums[0];
var r = Random.Shared.Next(nums.Length);
(nums[0], nums[r]) = (nums[r], nums[0]);
int lo = 1, hi = nums.Length - 1, v = nums[0];
while(true)
{
while (lo < hi && nums[lo] > v) lo++;
while (nums[hi] < v) hi--;
if (lo >= hi)
break;
(nums[lo], nums[hi]) = (nums[hi], nums[lo]);
lo++; hi--;
}
(nums[0], nums[hi]) = (nums[hi], nums[0]);
var wtf = hi - (k - 1);
return wtf switch
{
> 0 => FindKthLargest(nums[..hi], k),
< 0 => FindKthLargest(nums[(hi + 1)..], k - hi - 1),
_ => nums[hi]
};
//this works too:
/*
if (hi == k - 1)
return nums[hi];
return hi > k - 1 ?
FindKthLargest(nums[..hi], k) :
FindKthLargest(nums[(hi + 1)..], k - hi - 1);
*/
}
return hi - (k - 1) switch
{
...
};
这被解析为:
hi - ((k - 1) switch
{
...
});
也就是说,它查看
k - 1
,打开结果,然后从 hi
中减去该结果。
你想要:
return (hi - (k - 1)) switch
{
...
}
此行为由表达式/运算符优先级决定。更改为:
return (hi - (k - 1)) switch
{
> 0 => FindKthLargest(nums[..hi], k),
< 0 => FindKthLargest(nums[(hi + 1)..], k - hi - 1),
_ => nums[hi]
};
否则解析为:
hi - ((k-1) switch {...})
、
hi - (k - 1) switch
和
(hi - k + 1) switch
的反编译 (@sharplab.io)。
另请检查运算符优先级文档,其中:
/switch
:with
和switch
表达式with
优先级高于:
、x + y
:添加剂x – y