将列表<int>隐藏到范围列表

Wou*_*tor 2 c# list range

我需要将list整数转换为范围列表.

我有一个List<int>包含8, 22, 41.这些值是分节符,从一个完整的清单147.

我想获得一个包含起始行和结束行的范围列表.输出应该是

{(1,7),(8,21),(22,40),(41,47)}
Run Code Online (Sandbox Code Playgroud)

我试图从这个问题调整解决方案,但无法使其工作.

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

Eri*_*ert 5

即使程序有效,在查询期间应该变异本地的答案也是危险的; 不要陷入这个坏习惯.

解决问题的最简单方法是编写迭代器块.我们假设你有明显的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);
}
Run Code Online (Sandbox Code Playgroud)

这是解决这个问题的正确方法.如果你想变得有点傻,你可以使用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;
}
Run Code Online (Sandbox Code Playgroud)

现在我们有:

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;
}
Run Code Online (Sandbox Code Playgroud)

但与简单地直接编写迭代器块相比,这似乎是不必要的巴洛克式.此外,这会将集合重复两次,这被许多人认为是糟糕的风格.

一个解决问题的可爱方法是概括第一个解决方案:

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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在你的方法是:

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);
}
Run Code Online (Sandbox Code Playgroud)

我非常喜欢最后一个.也就是说,我们被给予8, 22, 41,我们构建1, 8, 22, 41, 48,然后从中我们挑选对和构造(1, 7), (8, 21),等等.