당신은 할 수 사용하는 쿼리의 번호를 사용 Take
하고Skip
,하지만 원래 목록에 너무 많은 반복을 추가, 저는 믿습니다.
오히려 다음과 같이 자신 만의 반복자를 만들어야한다고 생각합니다.
public static IEnumerable<IEnumerable<T>> GetEnumerableOfEnumerables<T>(
IEnumerable<T> enumerable, int groupSize)
{
// The list to return.
List<T> list = new List<T>(groupSize);
// Cycle through all of the items.
foreach (T item in enumerable)
{
// Add the item.
list.Add(item);
// If the list has the number of elements, return that.
if (list.Count == groupSize)
{
// Return the list.
yield return list;
// Set the list to a new list.
list = new List<T>(groupSize);
}
}
// Return the remainder if there is any,
if (list.Count != 0)
{
// Return the list.
yield return list;
}
}
그런 다음 이것을 호출하면 LINQ가 활성화되어 결과 시퀀스에서 다른 작업을 수행 할 수 있습니다.
Sam의 답변에 비추어 볼 때 , 나는 이것을하지 않고 이것을 수행하는 더 쉬운 방법이 있다고 느꼈습니다.
- 목록을 다시 반복합니다 (원래는하지 않았습니다)
- 청크를 해제하기 전에 그룹으로 항목을 구체화 (큰 청크의 경우 메모리 문제가 있음)
- Sam이 게시 한 모든 코드
즉 나는 여기에 확장 메서드에 성문화 한 또 다른 패스,의, 말했다 IEnumerable<T>
이라고는 Chunk
:
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source,
int chunkSize)
{
// Validate parameters.
if (source == null) throw new ArgumentNullException("source");
if (chunkSize <= 0) throw new ArgumentOutOfRangeException("chunkSize",
"The chunkSize parameter must be a positive value.");
// Call the internal implementation.
return source.ChunkInternal(chunkSize);
}
기본적인 오류 점검만으로 놀라운 것은 없습니다.
다음으로 넘어갑니다 ChunkInternal
.
private static IEnumerable<IEnumerable<T>> ChunkInternal<T>(
this IEnumerable<T> source, int chunkSize)
{
// Validate parameters.
Debug.Assert(source != null);
Debug.Assert(chunkSize > 0);
// Get the enumerator. Dispose of when done.
using (IEnumerator<T> enumerator = source.GetEnumerator())
do
{
// Move to the next element. If there's nothing left
// then get out.
if (!enumerator.MoveNext()) yield break;
// Return the chunked sequence.
yield return ChunkSequence(enumerator, chunkSize);
} while (true);
}
기본적으로 IEnumerator<T>
각 항목을 수동으로 반복합니다. 현재 열거 할 항목이 있는지 확인합니다. 각 청크가 열거 된 후 남은 항목이 없으면 나옵니다.
시퀀스에 항목이 있음을 감지하면 내부 IEnumerable<T>
구현에 대한 책임을 다음에 위임합니다 ChunkSequence
.
private static IEnumerable<T> ChunkSequence<T>(IEnumerator<T> enumerator,
int chunkSize)
{
// Validate parameters.
Debug.Assert(enumerator != null);
Debug.Assert(chunkSize > 0);
// The count.
int count = 0;
// There is at least one item. Yield and then continue.
do
{
// Yield the item.
yield return enumerator.Current;
} while (++count < chunkSize && enumerator.MoveNext());
}
이후 MoveNext
이미 호출 된 IEnumerator<T>
전달 ChunkSequence
, 그것은에 의해 반환 된 항목을 얻을 수 Current
있는지 결코 이상 반환 할 수있게되지 다음 수를 증가chunkSize
항목마다 반복 한 후 순서의 다음 항목으로 이동 (그러나 수의 경우 단락 생산량은 청크 크기를 초과합니다).
항목이 남아 있지 않으면 InternalChunk
메소드는 외부 루프에 다른 패스를 전달하지만 MoveNext
두 번째로 호출 되면 문서 (강조 광산)에 따라 여전히 false를 반환합니다 .
MoveNext가 컬렉션의 끝을 통과하면 열거자는 컬렉션의 마지막 요소 뒤에 위치하고 MoveNext는 false를 반환합니다. 열거자가이 위치에있을 때 ResetNext가 호출 될 때까지 MoveNext에 대한 후속 호출도 false를 반환합니다.
이 시점에서 루프가 중단되고 시퀀스 시퀀스가 종료됩니다.
이것은 간단한 테스트입니다.
static void Main()
{
string s = "agewpsqfxyimc";
int count = 0;
// Group by three.
foreach (IEnumerable<char> g in s.Chunk(3))
{
// Print out the group.
Console.Write("Group: {0} - ", ++count);
// Print the items.
foreach (char c in g)
{
// Print the item.
Console.Write(c + ", ");
}
// Finish the line.
Console.WriteLine();
}
}
산출:
Group: 1 - a, g, e,
Group: 2 - w, p, s,
Group: 3 - q, f, x,
Group: 4 - y, i, m,
Group: 5 - c,
중요한 점 은 전체 하위 시퀀스를 배수하거나 상위 시퀀스의 어느 시점에서든 중단하지 않으면 작동하지 않습니다. 사용 사례 당신이 소비하는 것입니다 경우에 중요한주의이지만, 모든를 시퀀스 시퀀스의 요소를 이것이 효과가 있습니다.
또한 Sam이 한 시점에서했던 것처럼 주문을 가지고 놀면 이상한 일을 할 것 입니다.