Первое усилие, просто a-z, затем aa-zz
public static IEnumerable<string> GetExcelColumns()
{
for (char c = 'a'; c <= 'z'; c++)
{
yield return c.ToString();
}
char[] chars = new char[2];
for (char high = 'a'; high <= 'z'; high++)
{
chars[0] = high;
for (char low = 'a'; low <= 'z'; low++)
{
chars[1] = low;
yield return new string(chars);
}
}
}
Обратите внимание, что это остановится на 'zz'. Конечно, здесь есть некоторое уродливое дублирование с точки зрения циклов. К счастью, это легко исправить - и это может быть еще более гибким:
Вторая попытка: более гибкий алфавит
private const string Alphabet = "abcdefghijklmnopqrstuvwxyz";
public static IEnumerable<string> GetExcelColumns()
{
return GetExcelColumns(Alphabet);
}
public static IEnumerable<string> GetExcelColumns(string alphabet)
{
foreach(char c in alphabet)
{
yield return c.ToString();
}
char[] chars = new char[2];
foreach(char high in alphabet)
{
chars[0] = high;
foreach(char low in alphabet)
{
chars[1] = low;
yield return new string(chars);
}
}
}
Теперь, если вы хотите сгенерировать только a, b, c, d, aa, ab, ac, ad, ba, ... вы бы позвонили GetExcelColumns("abcd")
.
Третья попытка (пересмотренная далее) - бесконечная последовательность
public static IEnumerable<string> GetExcelColumns(string alphabet)
{
int length = 0;
char[] chars = null;
int[] indexes = null;
while (true)
{
int position = length-1;
// Try to increment the least significant
// value.
while (position >= 0)
{
indexes[position]++;
if (indexes[position] == alphabet.Length)
{
for (int i=position; i < length; i++)
{
indexes[i] = 0;
chars[i] = alphabet[0];
}
position--;
}
else
{
chars[position] = alphabet[indexes[position]];
break;
}
}
// If we got all the way to the start of the array,
// we need an extra value
if (position == -1)
{
length++;
chars = new char[length];
indexes = new int[length];
for (int i=0; i < length; i++)
{
chars[i] = alphabet[0];
}
}
yield return new string(chars);
}
}
Возможно, это будет более чистый код с использованием рекурсии, но это будет не так эффективно.
Обратите внимание, что если вы хотите остановиться в определенной точке, вы можете просто использовать LINQ:
var query = GetExcelColumns().TakeWhile(x => x != "zzz");
«Перезапуск» итератора
Чтобы перезапустить итератор с заданной точки, вы действительно можете использовать SkipWhile
, как предлагает thesoftwarejedi. Это довольно неэффективно, конечно. Если вы можете сохранять любое состояние между вызовами, вы можете просто оставить итератор (для любого решения):
using (IEnumerator<string> iterator = GetExcelColumns())
{
iterator.MoveNext();
string firstAttempt = iterator.Current;
if (someCondition)
{
iterator.MoveNext();
string secondAttempt = iterator.Current;
// etc
}
}
В качестве альтернативы, вы в любом случае вполне можете структурировать свой код так, чтобы использовать foreach
, просто выделив первое значение, которое вы действительно можете использовать.