关于lambda表达式的简单问题
我想在以下代码中获取所有交易的平均值。我使用的公式是((价格 1*qty 1+(price 2*qty 2)....+(price n*qty n)/(qty 1+qty 2+...+qty n)
在下面的代码中,我使用 sum 函数来计算 (price*qty) 的总和,复杂度将是 O(n),再次将所有数量相加,复杂度将是 O(n)。那么,有什么方法可以使用复杂性 O(n) 表示可以计算两个结果的单个 lambda 表达式找到两者的总和。
使用 for 循环,我可以计算 O(n) 复杂度的两个结果。
class Program
{
static void Main(string[] args)
{
List<Trade> trades = new List<Trade>()
{
new Trade() {price=2,qty=2},
new Trade() {price=3,qty=3}
};
///using lambda
int price = trades.Sum(x => x.price * x.qty);
int qty = trades.Sum(x => x.qty);
///using for loop
int totalPriceQty=0, totalQty=0;
for (int i = 0; i < trades.Count; ++i)
{
totalPriceQty += trades[i].price * trades[i].qty;
totalQty += trades[i].qty;
}
Console.WriteLine("Average {0}", qty != 0 ? price / qty : 0);
Console.Read();
}
}
class Trade
{
public int price;
public int qty;
}
编辑:我知道系数不算数。让我改写这个问题,说使用 lambda,我们将遍历列表中的每个元素两次,而使用 for 循环,我们将只遍历每个元素一次。是否有任何使用 lambda 的解决方案,因此它不必两次遍历列表元素?
如前所述,无论您迭代一次还是两次,Big-O 都是不变的。如果只想使用 Linq 迭代一次,则可以使用自定义聚合器(由于要简化为相同的属性,因此我们可以只使用 Trade
实例进行聚合):
var ag = trades.Aggregate(new Trade(),
(agg, trade) =>
{
agg.price += trade.price * trade.qty;
agg.qty += trade.qty;
return agg;
});
int price = ag.price;
int qty = ag.qty;
在这一点上,我个人只会使用 foreach 循环或您已经拥有的简单 lambda - 除非性能在这里至关重要(测量它!
Big-O 复杂度不考虑常数系数。O(n) + O(n) 仍然给出 O(n)。
如果您确定要在 lambda 中使用它,下面是一个使用 Aggregate
运算符的示例。它看起来很做作,我不推荐它而不是传统的 for 循环。
var result = trades.Aggregate(
Tuple.Create(0, 0),
(acc, trade) =>
Tuple.Create(acc.Item1 + trade.price * trade.qty, acc.Item2 + trade.qty));
int totalPrice = result.Item1;
int totalQuantity = result.Item2;
为了扩展 Broken Glass 的答案,您还可以使用匿名类型作为聚合器,如下所示:
var result = trades.Aggregate(
new { TotalValue = 0L, TotalQuantity = 0L },
(acc, trade) => new
{
TotalValue = acc.TotalValue + trade.price,
TotalQuantity = acc.TotalQuantity + trade.qty
}
);
这种方法有两个小的优点:
如果您正在对大量交易(或大宗交易)进行这种计算,您可能会溢出跟踪交易总价值和总份额的
int
。 此方法允许您将long
指定为数据类型(因此溢出需要更长的时间)。与仅返回
Trade
对象相比,从此聚合返回的对象将具有更有意义的属性。
最大的缺点是看起来有点奇怪。