如何消除我的累加器中的条件



给定此代码:

private static IObservable<Stock> ToStock(this IObservable<decimal> prices, string symbol)
{
return prices.Scan(
default(Stock),
(previous, price) => previous == default(Stock)
? new Stock(symbol, price)
: previous.Change(price));
}
// The signature for Stock.Change() looks like this. Stock is an immutable class.
// public Stock Change(decimal newCurrentPrice)

我想消除每次调用累加器时发生的检查previous == default(Stock)。我所拥有的是第一个项目与其他项目的行为不同。我不知道如何简单地使用LINQ for Rx来表达这一点。

编辑。这是Stock的代码,这可能有助于解释为什么我不能给它一个价格的哨兵值。

public class Stock
{
private readonly decimal _current;
private readonly decimal _dayHigh;
private readonly decimal _dayLow;
private readonly decimal _dayOpen;
private readonly decimal _lastChange;
private readonly string _symbol;
public Stock(string symbol, decimal price)
{
if (symbol == null) throw new ArgumentNullException("symbol");
if (price <= 0) throw new ArgumentOutOfRangeException("price", "Price must be greater than zero.");
_symbol = symbol;
_current = _dayOpen = _dayLow = _dayHigh = price;
}
private Stock(Stock original, decimal newCurrent)
{
if (original == null) throw new ArgumentNullException("original");
_symbol = original.Symbol;
_current = newCurrent;
_dayOpen = original.DayOpen;
_dayHigh = Math.Max(newCurrent, original.DayHigh);
_dayLow = Math.Min(newCurrent, original.DayLow);
_lastChange = newCurrent - original.Current;
}
public string Symbol { get { return _symbol; } }
public decimal Current { get { return _current; } }
public decimal LastChange { get { return _lastChange; } }
public decimal DayOpen { get { return _dayOpen; } }
public decimal DayLow { get { return _dayLow; } }
public decimal DayHigh { get { return _dayHigh; } }
public decimal DayChange { get { return Current - DayOpen; } }
public double DayChangeRatio { get { return (double) Math.Round(DayChange/Current, 4); } }
public Stock Change(decimal newCurrent)
{
return newCurrent == Current
? this
: new Stock(this, newCurrent);
}
}

我想出了这个解决方案:

private static IObservable<Stock> ToStock2(this IObservable<decimal> prices, string symbol)
{
Func<Stock, decimal, Stock> accumulator = (_, firstPrice) =>
{
accumulator = (previous, price) => previous.Change(price);
return new Stock(symbol, firstPrice);
};
return prices.Scan(default(Stock), (previous, price) => accumulator(previous, price));
}

它在第一次调用时使用了一个自变异的Func变量来改变行为,但快速测试(以50万的价格运行)显示,它的执行速度比原始方法慢2-3%,代码也不那么清晰。看起来。NET在为每个项执行相等比较器方面比为每个项调用第二个Func更有效。我不确定是否有任何方法可以优化它,使其比原来的表现更好,从而证明清晰度降低的合理性。

您可以这样做:

public static partial class ObservableExtensions
{
public static IObservable<Stock> ToStock(this IObservable<decimal> prices, string symbol)
{                                                
return Observable.Create<Stock>(o =>
{
Stock lastStock;            
Action<decimal> action = null;
action = price => {
lastStock = new Stock(symbol, price);
action = newPrice =>
{
lastStock = lastStock.Change(newPrice);
o.OnNext(lastStock);
};
o.OnNext(lastStock);
};
return prices.Subscribe(p => action(p), o.OnError, o.OnCompleted);
});    
}   
}

与吉姆的回答相比,我不确定我的回答是否更好;这是一个类似的想法,但它避免了调用Scan,这可能会避免一些跳跃。

我的古怪性能测试表明,它的运行并不比原来的差,但也不比原来的好。我用100000000的价格跑了几次,他们的胜率在1%以内,每次都赢了大约一半的时间。没有统计学上的显著差异。

不过,我对此持保留态度,因为这是在我的家用电脑上,而不是在实验室环境中,不会运行很长时间,而且天知道还安装了什么其他服务。

然而。。。通过重写私有构造函数,使其不冗余地进行Math.Max/Min计算,并绕过属性直接访问字段,我确实获得了看似显著的3%的改进。我相信还有更多的好处需要探索,例如删除Change和使用公共字段:

private Stock(Stock original, decimal newCurrent)
{
if (original == null) throw new ArgumentNullException("original");
_symbol = original._symbol;
_current = newCurrent;
_dayOpen = original._dayOpen;        
if(newCurrent > original._dayHigh)
{
_dayHigh = newCurrent;
_dayLow = original._dayLow;            
}
else
{
_dayHigh = original._dayHigh;
_dayLow = newCurrent;          
}
_lastChange = newCurrent - original._current;
}

在总体性能方面,由于价格高昂,GC会对这种方法施加相当大的压力。我过去曾成功地在环形缓冲区中使用Stock实例池,该缓冲区是用数组实现的,以减少垃圾收集。

return prices.Skip(1)
.Scan(new Stock(symbol, prices.First()),
(previous, price) => previous.Change(price));

这能解决你的副作用问题吗?

我更愿意介绍某种多莫菲主义。您可以为种子目的介绍库存的特殊情况:

public class Stock {
// same implementation as yours but make the Change method virtual
public static Stock Seed(string symbol) {
return new StockSeed(symbol);
}
class StockSeed : Stock {
public StockSeed(string symbol) {
_symbol = symbol;
}
public override Stock Change(decimal newCurrent) {
return new Stock(Symbol, newCurrent)
}
}
}

然后您可以将反应式代码简化为:

static IObservable<Stock> ToStock(this IObservable<decimal> prices, string symbol)
{
return prices.Scan(Stock.Seed(symbol), (prev, price) => prev.Change(price));
}

相关内容

  • 没有找到相关文章