例如,我必须排序:
IEnumerable<float> List1 = new float[] {
0f, 0.1f, 0.5f, 0.9f, // < 1
1f, 1.1f, 1.5f, 1.9f, // < 2
2f, 2.9f // < 3
};
IEnumerable<int> List2 = new int[] {
1,
2,
3,
4
};
我需要得到下一个结果:
0f, 0.1f, 0.5f, 0.9f, 1,
1f, 1.1f, 1.5f, 1.9f, 2,
2f, 2.9f, 3,
4
我写了两个函数,但是它们并不懒惰!
private static IEnumerable<object> Merge(IEnumerable<float> list1, IEnumerable<int> list2) {
var queue1 = new Queue<float>( list1 ); // it is not lazy!!
var queue2 = new Queue<int>( list2 );
while (queue1.Any() && queue2.Any()) {
if (queue1.Peek() < queue2.Peek()) {
yield return queue1.Dequeue();
} else {
yield return queue2.Dequeue();
}
}
while (queue1.Any()) {
yield return queue1.Dequeue();
}
while (queue2.Any()) {
yield return queue2.Dequeue();
}
}
private static IEnumerable<object> Merge2(IEnumerable<float> list1, IEnumerable<int> list2) {
var queue1 = new Queue<float>( list1 ); // it is not lazy!!
foreach (var item2 in list2) {
while (queue1.Any() && queue1.Peek() < item2) {
yield return queue1.Dequeue();
}
yield return item2;
}
}
我该如何进行懒惰评估?
答案 0 :(得分:2)
我假设您有两个已经分别排序的序列,并且您想返回合并两个序列后的排序序列。该序列是延迟生成的,一次只读取每个序列中的下一个元素。
private static IEnumerable<object> Merge(IEnumerable<float> sequence1, IEnumerable<int> sequence2)
{
// Get enumerators for iterating through the two sequences.
using (var enumerator1 = sequence1.GetEnumerator())
using (var enumerator2 = sequence2.GetEnumerator())
{
var remaining1 = enumerator1.MoveNext();
var remaining2 = enumerator2.MoveNext();
while (remaining1 && remaining2)
{
if (enumerator1.Current < enumerator2.Current)
{
yield return enumerator1.Current;
remaining1 = enumerator1.MoveNext();
}
else
{
yield return enumerator2.Current;
remaining2 = enumerator2.MoveNext();
}
}
if (remaining1)
{
do { yield return enumerator1.Current; }
while (enumerator1.MoveNext());
}
else if (remaining2)
{
do { yield return enumerator2.Current; }
while (enumerator2.MoveNext());
}
}
}
更新:基于Enigmativity的评论,您可以将其推广为可与任何可比较的类型一起使用:
private static IEnumerable<T> Merge<T>(IEnumerable<T> sequence1, IEnumerable<T> sequence2, IComparer<T> comparer = null)
{
if (comparer == null)
comparer = Comparer<T>.Default;
// Get enumerators for iterating through the two sequences.
using (var enumerator1 = sequence1.GetEnumerator())
using (var enumerator2 = sequence2.GetEnumerator())
{
var remaining1 = enumerator1.MoveNext();
var remaining2 = enumerator2.MoveNext();
while (remaining1 && remaining2)
{
if (comparer.Compare(enumerator1.Current, enumerator2.Current) < 0)
{
yield return enumerator1.Current;
remaining1 = enumerator1.MoveNext();
}
else
{
yield return enumerator2.Current;
remaining2 = enumerator2.MoveNext();
}
}
if (remaining1)
{
do { yield return enumerator1.Current; }
while (enumerator1.MoveNext());
}
else if (remaining2)
{
do { yield return enumerator2.Current; }
while (enumerator2.MoveNext());
}
}
}
但是,对于OP将float[]
与int[]
合并的示例,则需要转换第二个列表:
var result = Merge(List1, List2.Select(x => (float)x));
答案 1 :(得分:0)
我为IEnumerator<T>
和更漂亮的Merge
方法制作了一个舒适的包装纸。
public static IEnumerable<object> Merge(IEnumerable<float> list1, IEnumerable<int> list2) {
using (var enumerator1 = new SmartEnumerator<float>( list1.GetEnumerator() ))
using (var enumerator2 = new SmartEnumerator<int>( list2.GetEnumerator() )) {
while (!enumerator1.IsCompleted && !enumerator2.IsCompleted) {
if (enumerator1.Peek() < enumerator2.Peek()) {
yield return enumerator1.Pull();
} else {
yield return enumerator2.Pull();
}
}
while (!enumerator1.IsCompleted) {
yield return enumerator1.Pull();
}
while (!enumerator2.IsCompleted) {
yield return enumerator2.Pull();
}
}
}
public class SmartEnumerator<T> : IDisposable {
private readonly IEnumerator<T> target;
private bool hasValue;
private T Value => target.Current;
public bool IsCompleted {
get {
RequestNextValueIfNeeded();
return !hasValue;
}
}
public SmartEnumerator(IEnumerator<T> target) {
this.target = target;
}
public void Dispose() {
target.Dispose();
}
public T Peek() {
if (IsCompleted && !hasValue) throw new InvalidOperationException( "Enumerator is already completed" );
RequestNextValueIfNeeded();
return Value;
}
public T Pull() {
if (IsCompleted && !hasValue) throw new InvalidOperationException( "Enumerator is already completed" );
RequestNextValueIfNeeded();
hasValue = false;
return Value;
}
// Helpers
private void RequestNextValueIfNeeded() {
if (!hasValue) RequestNextValue();
}
private void RequestNextValue() {
hasValue = target.MoveNext();
}
}