您的代码在准确性方面是正确的(十进制在反转其符号时不会降低精度)。通过使用循环编写它并进行手动完全外部合并连接,它的速度肯定可以提高 20 倍。但是,如果性能很关键,我只会推荐这个,因为这会使代码大小增加 5 倍。
编辑:合并连接意味着并排获取两个排序列表,并以可以找到相等元素的方式沿着两个列表遍历,即使两个数组中都有多余的元素。这是合并排序中合并步骤的扩展。完全外部意味着它在数据库中的含义:保留仅在其中一个数组中的值。
唯一的要求,排序列表,是给定的,因为您对结果列表进行排序。
您的代码将像这样工作:
var results = new List<decimal>(input.Select(x => x*100 ));
results.Sort();
var listA = new List<decimal>();
var listB = new List<decimal>();
//now to the merge-join and fill listA and B
merge-join 实现起来会很复杂,但为了让您入门,这里是一个基于迭代器的版本,我在项目中使用它来连接两个我永远无法加载到内存但已排序的巨大文件之间的连接。
public static IEnumerable<TResult> MergeJoin_OneToOne<TLeft, TRight, TResult>(
IEnumerable<TLeft> leftCollection,
IEnumerable<TRight> rightCollection,
Func<TLeft, TRight, int> comparison,
Func<TLeft, TResult> onlyLeftSelector,
Func<TRight, TResult> onlyRightSelector,
Func<TLeft, TRight, TResult> bothSelector)
{
return MergeJoin_OneToOne_Impl(leftCollection, rightCollection, comparison, onlyLeftSelector, onlyRightSelector, bothSelector);
}
static IEnumerable<TResult> MergeJoin_OneToOne_Impl<TLeft, TRight, TResult>(
IEnumerable<TLeft> leftCollection,
IEnumerable<TRight> rightCollection,
Func<TLeft, TRight, int> comparison,
Func<TLeft, TResult> onlyLeftSelector,
Func<TRight, TResult> onlyRightSelector,
Func<TLeft, TRight, TResult> bothSelector)
{
if (leftCollection == null) throw new ArgumentNullException("leftCollection");
if (rightCollection == null) throw new ArgumentNullException("rightCollection");
if (comparison == null) throw new ArgumentNullException("comparison");
if (onlyLeftSelector == null) throw new ArgumentNullException("onlyLeftSelector");
if (onlyRightSelector == null) throw new ArgumentNullException("onlyRightSelector");
if (bothSelector == null) throw new ArgumentNullException("bothSelector");
using (var leftEnum = leftCollection.GetEnumerator())
using (var rightEnum = rightCollection.GetEnumerator())
{
if (!leftEnum.MoveNext())
{
while (rightEnum.MoveNext()) yield return onlyRightSelector(rightEnum.Current);
yield break;
}
if (!rightEnum.MoveNext())
{
do
{
yield return onlyLeftSelector(leftEnum.Current);
} while (leftEnum.MoveNext());
yield break;
}
while (true)
{
int cmp = comparison(leftEnum.Current, rightEnum.Current);
if (cmp == 0)
{
yield return bothSelector(leftEnum.Current, rightEnum.Current);
if (!leftEnum.MoveNext())
{
while (rightEnum.MoveNext())
{
yield return onlyRightSelector(rightEnum.Current);
}
yield break;
}
if (!rightEnum.MoveNext())
{
do
{
yield return onlyLeftSelector(leftEnum.Current);
} while (leftEnum.MoveNext());
yield break;
}
}
else if (cmp < 0)
{
yield return onlyLeftSelector(leftEnum.Current);
if (!leftEnum.MoveNext())
{
do
{
yield return onlyRightSelector(rightEnum.Current);
} while (rightEnum.MoveNext());
yield break;
}
}
else
{
yield return onlyRightSelector(rightEnum.Current);
if (!rightEnum.MoveNext())
{
do
{
yield return onlyLeftSelector(leftEnum.Current);
} while (leftEnum.MoveNext());
yield break;
}
}
}
}
}
出于性能原因,您将此函数专门用于IList<decimal> 元素。踢掉迭代器的东西并使用两个整数来表示列表中的当前位置。遍历 listA 时忽略 lt 0 且 B 相同的元素。
速度提升将来自多项改进:对象分配大大减少;由 except 执行的散列操作消失了;没有 lambda,也没有间接。