我很感兴趣的是,在LINQ中,是否有一种方法可以检查列表中的所有数字是否单调递增?
示例
List<double> list1 = new List<double>() { 1, 2, 3, 4 };
Debug.Assert(list1.IsIncreasingMonotonically() == true);
List<double> list2 = new List<double>() { 1, 2, 100, -5 };
Debug.Assert(list2.IsIncreasingMonotonically() == false);
我问这个问题的原因是,我想知道将列表中的一个元素与其前一个元素进行比较的技术,这是我在使用LINQ时从未理解过的。
C#中已完成的示例类
根据下面@Servy
的官方回答,这是我现在使用的完整课程。它将扩展方法添加到项目中,以检查列表是单调增加还是严格单调减少。我正在努力适应函数式编程风格,这是一种很好的学习方式。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyHelper
{
/// <summary>
/// Classes to check if a list is increasing or decreasing monotonically. See:
/// http://stackoverflow.com/questions/14815356/is-it-possible-to-use-linq-to-check-if-all-numbers-in-a-list-are-increasing-mono#14815511
/// Note the difference between strictly monotonic and monotonic, see:
/// http://en.wikipedia.org/wiki/Monotonic_function
/// </summary>
public static class IsMonotonic
{
/// <summary>
/// Returns true if the elements in the are increasing monotonically.
/// </summary>
/// <typeparam name="T">Type of elements in the list.</typeparam>
/// <param name="list">List we are interested in.</param>
/// <returns>True if all of the the elements in the list are increasing monotonically.</returns>
public static bool IsIncreasingMonotonically<T>(this List<T> list) where T : IComparable
{
return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) <= 0).All(b => b);
}
/// <summary>
/// Returns true if the elements in the are increasing strictly monotonically.
/// </summary>
/// <typeparam name="T">Type of elements in the list.</typeparam>
/// <param name="list">List we are interested in.</param>
/// <returns>True if all of the the elements in the list are increasing monotonically.</returns>
public static bool IsIncreasingStrictlyMonotonically<T>(this List<T> list) where T : IComparable
{
return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) < 0).All(b => b);
}
/// <summary>
/// Returns true if the elements in the are decreasing monotonically.
/// </summary>
/// <typeparam name="T">Type of elements in the list.</typeparam>
/// <param name="list">List we are interested in.</param>
/// <returns>True if all of the the elements in the list are decreasing monotonically.</returns>
public static bool IsDecreasingMonotonically<T>(this List<T> list) where T : IComparable
{
return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) >= 0).All(b => b);
}
/// <summary>
/// Returns true if the elements in the are decreasing strictly monotonically.
/// </summary>
/// <typeparam name="T">Type of elements in the list.</typeparam>
/// <param name="list">List we are interested in.</param>
/// <returns>True if all of the the elements in the list are decreasing strictly monotonically.</returns>
public static bool IsDecreasingStrictlyMonotonically<T>(this List<T> list) where T : IComparable
{
return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) > 0).All(b => b);
}
/// <summary>
/// Returns true if the elements in the are increasing monotonically.
/// </summary>
/// <typeparam name="T">Type of elements in the list.</typeparam>
/// <param name="list">List we are interested in.</param>
/// <returns>True if all of the the elements in the list are increasing monotonically.</returns>
public static bool IsIncreasingMonotonicallyBy<T>(this List<T> list, Func<T> x) where T : IComparable
{
return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) <= 0).All(b => b);
}
public static void UnitTest()
{
{
List<double> list = new List<double>() { 1, 2, 3, 4 };
Debug.Assert(list.IsIncreasingMonotonically<double>() == true);
Debug.Assert(list.IsIncreasingStrictlyMonotonically<double>() == true);
Debug.Assert(list.IsDecreasingMonotonically<double>() == false);
Debug.Assert(list.IsDecreasingStrictlyMonotonically<double>() == false);
}
{
List<double> list = new List<double>() { 1, 2, 100, -5 };
Debug.Assert(list.IsIncreasingMonotonically() == false);
Debug.Assert(list.IsIncreasingStrictlyMonotonically() == false);
Debug.Assert(list.IsDecreasingMonotonically() == false);
Debug.Assert(list.IsDecreasingStrictlyMonotonically() == false);
}
{
List<double> list = new List<double>() {1, 1, 2, 2, 3, 3, 4, 4};
Debug.Assert(list.IsIncreasingMonotonically() == true);
Debug.Assert(list.IsIncreasingStrictlyMonotonically<double>() == false);
Debug.Assert(list.IsDecreasingMonotonically() == false);
Debug.Assert(list.IsDecreasingStrictlyMonotonically() == false);
}
{
List<double> list = new List<double>() { 4, 3, 2, 1 };
Debug.Assert(list.IsIncreasingMonotonically() == false);
Debug.Assert(list.IsIncreasingStrictlyMonotonically<double>() == false);
Debug.Assert(list.IsDecreasingMonotonically() == true);
Debug.Assert(list.IsDecreasingStrictlyMonotonically() == true);
}
{
List<double> list = new List<double>() { 4, 4, 3, 3, 2, 2, 1, 1 };
Debug.Assert(list.IsIncreasingMonotonically() == false);
Debug.Assert(list.IsIncreasingStrictlyMonotonically<double>() == false);
Debug.Assert(list.IsDecreasingMonotonically() == true);
Debug.Assert(list.IsDecreasingStrictlyMonotonically() == false);
}
}
}
}
public static bool IsIncreasingMontonically<T>(List<T> list)
where T : IComparable
{
return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) <= 0)
.All(b => b);
}
请注意,这会对序列进行两次迭代。对于List
,这根本不是问题,对于IEnumerable
或IQueryable
,这可能很糟糕,所以在将List<T>
更改为IEnumerable<T>
之前要小心。
您不使用OrderBy()
对列表进行排序并将其与原始列表进行比较吗?如果他们是一样的,那么它会给你的答案伪说:
var increasing = orignalList.OrderBy(m=>m.value1).ToList();
var decreasing = orignalList.OrderByDescending(m=>m.value1).ToList();
var mono = (originalList == increasing || originalList == decreasing)
这里有一个将工作的单行:
var isIncreasing = list.OrderBy(x => x).SequenceEqual(list);
或者,如果你想提高性能,这里有一个单行线,它只会遍历列表一次,一旦到达一个无序的元素就退出:
var isIncreasing = !list.SkipWhile((x, i) => i == 0 || list[i - 1] <= x).Any();
通过使用Enumerable.Aggregate
方法:
list1.Aggregate((a, i) => a > i ? double.MaxValue : i) != double.MaxValue;
使用循环!它简短、快速且可读。除了Servy的答案外,这个线程中的大多数解决方案都不必要地慢(排序需要"n log n"时间)。
// Test whether a sequence is strictly increasing.
public bool IsIncreasing(IEnumerable<double> list)
{
bool initial = true;
double last = Double.MinValue;
foreach(var x in list)
{
if (!initial && x <= last)
return false;
initial = false;
last = x;
}
return true;
}
示例
IsIncreasing(new List<double>{1,2,3})
返回TrueIsIncreasing(new List<double>{1,3,2})
返回False
如果您想检查列表是否总是从一个索引增加到另一个索引:
IEnumerable<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 10 };
bool allIncreasing = !list
.Where((i, index) => index > 0 && list.ElementAt(index - 1) >= i)
.Any();
演示
但在我看来,在这种情况下,一个简单的循环会更可读。
考虑下面这样的实现,它只枚举给定的IEnumerable一次。枚举可能会产生副作用,如果可能的话,调用者通常会期望一次通过。
public static bool IsIncreasingMonotonically<T>(
this IEnumerable<T> _this)
where T : IComparable<T>
{
using (var e = _this.GetEnumerator())
{
if (!e.MoveNext())
return true;
T prev = e.Current;
while (e.MoveNext())
{
if (prev.CompareTo(e.Current) > 0)
return false;
prev = e.Current;
}
return true;
}
}
public static class EnumerableExtensions
{
private static bool CompareAdjacentElements<TSource>(this IEnumerable<TSource> source,
Func<TSource, TSource, bool> comparison)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
throw new ArgumentException("The input sequence is empty", "source");
var previous = iterator.Current;
while (iterator.MoveNext())
{
var next = iterator.Current;
if (comparison(previous, next)) return false;
previous = next;
}
return true;
}
}
public static bool IsSorted<TSource>(this IEnumerable<TSource> source)
where TSource : IComparable<TSource>
{
return CompareAdjacentElements(source, (previous, next) => previous.CompareTo(next) > 0);
}
public static bool IsSorted<TSource>(this IEnumerable<TSource> source, Comparison<TSource> comparison)
{
return CompareAdjacentElements(source, (previous, next) => comparison(previous, next) > 0);
}
public static bool IsStrictSorted<TSource>(this IEnumerable<TSource> source)
where TSource : IComparable<TSource>
{
return CompareAdjacentElements(source, (previous, next) => previous.CompareTo(next) >= 0);
}
public static bool IsStrictSorted<TSource>(this IEnumerable<TSource> source, Comparison<TSource> comparison)
{
return CompareAdjacentElements(source, (previous, next) => comparison(previous, next) >= 0);
}
}