【发布时间】:2011-05-23 16:37:27
【问题描述】:
大家! 如何在 C# 中获取特定范围内的 int 数组的最小值? 例如: 整数 [] 数组 = 新整数 {1,2,3,4,5,6,7,8,76,45}; 我想在第 3 和第 8 个元素之间获得一个最小值。 也许可以通过 LINQ 查询获得?
【问题讨论】:
-
是否总是至少有 8 个元素?
大家! 如何在 C# 中获取特定范围内的 int 数组的最小值? 例如: 整数 [] 数组 = 新整数 {1,2,3,4,5,6,7,8,76,45}; 我想在第 3 和第 8 个元素之间获得一个最小值。 也许可以通过 LINQ 查询获得?
【问题讨论】:
int min = array.Where((value, index) => index >= 2 && index <= 7).Min();
编辑
实际上,上面的方法效率很低,因为它枚举了整个序列,即使我们对索引高于 7 的项目不感兴趣。更好的解决方案是使用TakeWhile:
int min = array.TakeWhile((value, index) => index <= 7).Skip(2).Min();
不幸的是,它的可读性不是很好......让它变得更好的最佳选择可能是创建一个自定义扩展方法,如 Jon 的回答所示。
【讨论】:
array.Skip(2).Take(5).Min();
【讨论】:
Skip 和Take 可以立即清楚地表明元素的值 与过滤无关......而更一般的Where 子句迫使读者考虑仔细看看到底发生了什么。请注意,对于整数数组,也很容易意外地以错误的方式获取 value 和 index 参数。如果你想让它更“开始”和“结束”相关,那么 Eric 的方法是一个不错的选择。
Where 子句会强制评估每一对 - 这使得它的效率可能远低于Skip/Take 解决方案。如果数组中有一百万个项目,它需要考虑所有项目......而不是只是跳过一些,取走需要的东西,然后继续。
int[] arr = {0,1,2,3,4,5,6,7,8};
int start = 3;
int end = 8;
int min = arr.Skip(start - 1).Take(end - start).Min();
【讨论】:
array.Skip(3).Take(4).Min();
【讨论】:
只是添加另一个选项:
int start = 3;
int end = 8;
var min = Enumerable.Range(start - 1,end - start).Select(idx => array[idx]).Min();
AFAIK,如果你必须在一个接近末尾的范围内取一个范围,这“理论上”会更快,而且你的数组真的很长。
那是因为(再次 AFAIK)Skip() 没有考虑到它是一个数组(即可以在 O(1) 中随机访问)并且无论如何都会枚举它。
【讨论】:
IList<T>)的特定情况下它更快 - 但在一般序列情况下它当然会崩溃。当然,Skip 也可能在未来的版本中得到修复 :)
我想我不妨在这上面加上我的 tuppence。由于 Jason 反对我们说的是跳过了多少而不是结束索引,因此我们可以添加一个简单的扩展方法:
public static IEnumerable<T> WithIndexBetween<T>(this IEnumerable<T> source,
int startInclusive, int endExclusive)
{
// The two values can be the same, yielding no results... but they must
// indicate a reasonable range
if (endExclusive < startInclusive)
{
throw new ArgumentOutOfRangeException("endExclusive");
}
return source.Skip(startInclusive).Take(endExclusive - startInclusive);
}
然后:
int min = array.WithIndexBetween(2, 7).Min();
根据口味调整扩展方法名称。 (命名很难,我不会花很多时间在这里想出一个好名字:)
【讨论】:
就个人而言,我更喜欢这个:
public static class ArrayExtensions {
public static bool ArrayAndIndexesAreValid(
T[] array,
int startInclusive,
int endExclusive
) {
return array != null &&
array.Length > 0 &&
startInclusive >= 0 && startInclusive < array.Length &&
endExclusive >= 1 && endExclusive <= array.Length &&
startInclusive < endExclusive;
}
public static IEnumerable<T> Slice<T>(
this T[] array,
int startInclusive,
int endExclusive
) {
Contract.Requires<ArgumentException>(ArrayAndIndexesAreValid(
array,
startInclusive,
endExclusive)
);
for (int index = startInclusive; index < endExclusive; index++) {
yield return array[index];
}
}
public static T MinimumInIndexRange<T>(
this T[] array,
int startInclusive,
int endExclusive
) where T : IComparable {
Contract.Requires<ArgumentException>(ArrayAndIndexesAreValid(
array,
startInclusive,
endExclusive)
);
return array.Slice(startInclusive, endExclusive).Min();
}
public static T MaximumInIndexRange<T>(
this T[] array,
int startInclusive,
int endExclusive
) where T : IComparable {
Contract.Requires<ArgumentException>(ArrayAndIndexesAreValid(
array,
startInclusive,
endExclusive)
);
return array.Slice(startInclusive, endExclusive).Max();
}
}
【讨论】:
int.MaxValue,这可能不是一个好主意。它还混合了“找到最小值”和“考虑数组的一部分”的逻辑,而基于 LINQ 的解决方案没有。如果您需要找到数组切片的 最大值 值,您将重复所有切片逻辑。
long[] 等的最小值,您还强迫自己重新实现它。使此操作通用是一个简单的 IMO。当然,当你要返回一个空数组切片的值时,很难做到这一点......
Contract.Requires)。我会在一分钟内回复你的第二条评论。
Random.Next)使用包含的下限和独占的上限。