Ответы, в которых указано, что вы должны изменять локальный код во время запроса, опасны, даже если программа работает; не впадайте в эту дурную привычку.
Самый простой способ решить эту проблему - написать блок итератора. Предположим, у вас есть очевидный тип 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;
}
Но это кажется ненужным барокко по сравнению с простой записью блока итератора напрямую. Кроме того, это повторяет коллекцию дважды, что, по мнению многих, является плохим стилем.
Милый способ решения проблемы - обобщить первое решение:
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),
и т. Д.