第一次尝试,只有 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");
“重启”迭代器
要从给定点重新启动迭代器,您确实可以按照 thesoftwarejedi 的建议使用 SkipWhile。当然,这是相当低效的。如果您能够在调用之间保持任何状态,则可以只保留迭代器(对于任一解决方案):
using (IEnumerator<string> iterator = GetExcelColumns())
{
iterator.MoveNext();
string firstAttempt = iterator.Current;
if (someCondition)
{
iterator.MoveNext();
string secondAttempt = iterator.Current;
// etc
}
}
或者,您也可以将代码构造为使用foreach,只需突破您实际可以使用的第一个值。