如何使用LINQ, c#检查整数序列是否有偶数和奇数交替



假设c#中有一个int类型的列表:

List<int> numbers = new List<int>() { 1, 2, 7, 20, 3 };

是否有一种方法来检查它是否有交替奇数甚至和其中的数字(如上面的例子:如果一个如果它们是偶数那么下一个必须是奇数,反之亦然)?

我知道在循环中检查它很简单,但我正试图使用LINQ来实现这一点和扩展方法。

让我们分析一下这个问题。交替宇称是什么意思?

index : value : value + index
------------------------------
0 :     1 : 1 - note, all sums are odd
1 :     2 : 3
2 :     7 : 9
....

index : value : value + index
------------------------------
0 :     2 : 2 - note, all sums are even
1 :     1 : 2
2 :     6 : 8
....

你可以看到(你可以很容易地证明它)交替奇偶校验意味着index + value和为alloddalleven。让我们用Linq:

检查一下
List<int> numbers = new List<int>() { 1, 2, 7, 20, 3, 79 };
bool result = numbers
.DefaultIfEmpty()
.Select((item, index) => Math.Abs((long)item + (long)index))
.Aggregate((s, a) => s % 2 == a % 2 ? s % 2 : -1) >= 0;

实现注意事项:

  1. DefaultIfEmpty()-空序列全部(全部为零)值交替;然而,Aggregate没有什么可聚合的,并抛出异常。让我们把空序列变成一个元素序列。
  2. (long)为了防止整数溢出(int.MaxValue + index可以很好地超出int范围)
  3. Math.Abs: c#可以返回负余数(例如-1 % 2);我们不希望对此进行额外的检查,所以让我们使用绝对值
  4. 然而,我们可以在最终的Aggregate中利用这个效应(-1 % 2 == -1)。

扩展方法的解决方案我希望更容易理解:

public static bool IsAlternating(this IEnumerable<int> source) {
if (null == source)
throw new ArgumentNullException(nameof(source));
bool expectedZero = false;
bool first = true;
foreach (int item in source) {
int actual = item % 2;
if (first) {
first = false;
expectedZero = actual == 0;
} 
else if (actual == 0 && !expectedZero || actual != 0 && expectedZero) 
return false;  
expectedZero = !expectedZero;
} 
return true;
}

注意,循环解决方案(扩展方法)更有效:当模式不满足时,它立即返回false

您可以这样使用LINQ。您可以检查在加号位置是否有最后一个偶数项,或者在偶数位置是否有最后一个奇数项。

List<int> numbers = new List<int>() { 1, 2, 7, 20, 3 };
var temp = numbers.Where((x, i) => (i % 2 == 0 && x % 2 == 0) || (i % 2 == 1 && x % 2 == 1)).Take(1);
int count = temp.Count();
if(count == 0)
{
//true
}
else
{
//false
}

注意:假设你期望在偶数位得到偶数,在奇数位得到奇数。

您可以使用Aggregate来确定序列是否交替。

假设有0或1个元素,则结果为真。你可以随意修改这个逻辑。

在此基础上可以创建一个扩展方法:
public static bool IsAlternatingParitySequenceVerbose(this IEnumerable<int> col)
{
// state:
// -1 - sequence is not alternating
//  0 - even
//  1 - odd
if (!col.Any())
return true;
//check if first value is even
var firstState = Math.Abs(col.First() % 2);
var IsAlternating = col.Skip(1).Aggregate(firstState, (state, val) =>
{
if (state == -1)
return -1;
var current = Math.Abs(val % 2);
if (current == state)
return -1;
return current;
});
return IsAlternating != -1;
}

然后写一行字:

public static bool IsAlternatingParitySequence(this IEnumerable<int> col) =>
!col.Any()
|| col.Skip(1).Aggregate(Math.Abs(col.First() % 2), (state, val) =>
state == -1
? -1
: Math.Abs(val % 2) is var current && current == state
? -1
: current
) != -1;

最新更新