c# - 将 List<int> 转换为范围列表

标签 c# list range

我需要转换 list整数到范围列表。

我有一个 List<int>包含 8, 22, 41 .这些值是来自 1 的完整列表中的分节符47 .

我正在尝试获取包含起始行和结束行的范围列表。输出应该是

{(1,7),(8,21),(22,40),(41,47)}

我已尝试从 question调整解决方案但无法让它工作。

看起来应该很简单,但也许不是。

最佳答案

即使程序有效,在查询期间您应该改变局部变量的答案也是危险的;不要养成这个坏习惯。

解决问题的最简单方法是编写一个迭代器 block 。假设您有明显的 Pair<T>类型;在 C# 7 中,您可能会使用元组;调整它以使用元组是一个简单的练习:

static IEnumerable<Pair<int>> MakeIntervals(
  /* this */ IEnumerable<int> dividers, /* You might want an extension method.*/
  int start,
  int end)
{
  // Precondition: dividers is not null, but may be empty.
  // Precondition: dividers is sorted.  
  // If that's not true in your world, order it here.
  // Precondition: dividers contains no value equal to or less than start.
  // Precondition: dividers contains no value equal to or greater than end.
  // If it is possible for these preconditions to be violated then
  // the problem is underspecified; say what you want to happen in those cases.
  int currentStart = start;
  for (int divider in dividers) 
  {
    yield return new Pair<int>(currentStart, divider - 1);
    currentStart = divider;
  }
  yield return new Pair<int>(currentStart, end);
}

这是解决这个问题的正确方法。如果你想变得有点傻,你可以使用 Zip .从两个有用的扩展方法开始:

static IEnumerable<T> Prepend<T>(this IEnumerable<T> items, T first)
{
  yield return first;
  foreach(T item in items) yield return item;
}
static IEnumerable<T> Append<T>(this IEnumerable<T> items, T last)
{
  foreach(T item in items) yield return item;
  yield return last;
}

现在我们有了:

static IEnumerable<Pair<int>> MakeIntervals(
  IEnumerable<int> dividers,
  int start,
  int end)
{
  var starts = dividers.Prepend(start);
  // This is the sequence 1, 8, 22, 41
  var ends = dividers.Select(d => d - 1).Append(end);
  // This is the sequence 7, 21, 40, 47
  var results = starts.Zip(ends, (s, e) => new Pair<int>(s, e));
  // Zip them up: (1, 7), (8, 21), (22, 40), (41, 47)
  return results;
}

但是与直接编写迭代器 block 相比,这似乎是不必要的巴洛克式。此外,这会迭代两次集合,这被许多人认为是不好的风格。

解决问题的一个可爱方法是推广第一个解决方案:

static IEnumerable<R> SelectPairs<T, R>(
  this IEnumerable<T> items,
  IEnumerable<T, T, R> selector
)
{
  bool first = true;
  T previous = default(T);
  foreach(T item in items) {
    if (first) {
      previous = item;
      first = false;
    }
    else
    {
      yield return selector(previous, item);
      previous = item;
    }
  }
}

现在你的方法是:

static IEnumerable<Pair<int>> MakeIntervals(
  IEnumerable<int> dividers,
  int start,
  int end)
{
  return dividers
    .Prepend(start)
    .Append(end + 1)
    .SelectPairs((s, e) => new Pair<int>(s, e - 1);
}

我很喜欢最后一张。也就是说,我们得到 8, 22, 41 ,我们构造1, 8, 22, 41, 48 ,然后我们从中挑选出对并构造 (1, 7), (8, 21),等等。

关于c# - 将 List<int> 转换为范围列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52084106/

相关文章:

c# - 基于文本树创建 XML

python - 如何加入列表中的某些项目

python - 执行 for 循环的最佳方法是什么?

Vim 拉动行范围

algorithm - 在 Perl 中确定范围重叠的最快方法

c# - 可以在 C# 中使用带有 CA 证书的 .p12 文件而不将它们导入证书存储

c# - IHttpAsyncHandler.EndProcessRequest 中的 IAsyncResult 可能为 null 吗?

javascript - document.execCommand 在具有折叠范围的 IE 中无法正常工作(用户选择)

c# - 开放类型可以是结构吗?

r - 我们又来了 : append an element to a list in R