鉴于此代码:
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来表达。
EDIT。这是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);
}
}
答案 0 :(得分:1)
我提出了这个解决方案:
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
。我不确定是否还有优化它,以便它比原始表现更好,以证明清晰度降低。
答案 1 :(得分:1)
你可以这样做:
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);
});
}
}
与吉姆的回答相比,我不确定我的回答是否更好;这是一个类似的想法,但它避免调用扫描,这可能会避免一些跳跃。
我的flakey性能测试表明,这种情况并不比原版差 - 但也没有更好。我以100,000,000的价格运行了几次,他们相互之间的比例在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实例池来减少垃圾收集,从而取得了成功。
答案 2 :(得分:0)
return prices.Skip(1)
.Scan(new Stock(symbol, prices.First()),
(previous, price) => previous.Change(price));
这是否可以解决您的副作用问题?
答案 3 :(得分:0)
我更愿意介绍某种多态性。您可以出于种子目的介绍股票的特例:
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));
}