Первая попытка, сначала az, потом 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
в любом случае, просто вырываясь из первого значения, которое вы действительно можете использовать.
person
Jon Skeet
schedule
18.06.2009