Ответ 1
Вот что я придумал:
void Main()
{
var lists = new string[] { "123", "456", "789" };
foreach (var seq in RotateGrid(lists))
Console.WriteLine(string.Join(", ", seq));
}
public IEnumerable<IEnumerable<T>> RotateGrid<T>(IEnumerable<IEnumerable<T>> grid)
{
int rows = grid.Count();
int cols = grid.First().Count();
return
from i in Enumerable.Range(0, rows + cols - 1)
select (
from j in Enumerable.Range(0, i + 1)
where i - j < rows && j < cols
select grid.ElementAt(i - j).ElementAt(j)
);
}
Вывод:
1
4, 2
7, 5, 3
8, 6
9
Это становится намного более чистым и более эффективным, если вы можете принять IList<T>
вместо просто IEnumerable<T>
. Я думаю о более эффективном подходе (не используя .ElementAt
), который также работал бы с IEnumerable`, который я опубликую, если мне удастся его написать.
Обновление
Вот моя более практичная версия, которая по-прежнему справляется с обувным рогом в большом количестве linq. Это достаточно эффективный алгоритм, поскольку он только создает перечислитель один раз для каждого IEnumerable
.
public IEnumerable<IEnumerable<T>> RotateGrid<T>(IEnumerable<IEnumerable<T>> grid)
{
var enumerators = new LinkedList<IEnumerator<T>>();
var diagonal =
from e in enumerators
where e.MoveNext()
select e.Current;
foreach (var row in grid)
{
enumerators.AddFirst(row.GetEnumerator());
yield return diagonal.ToArray();
}
T[] output;
while (true)
{
output = diagonal.ToArray();
if(output.Length == 0) yield break;
yield return output;
}
}