我创建了一个非常简单的逻辑门模拟器,它可以在输入端采用IEnumerable馈送,并流经一系列门,在输出端准确地产生高/低位。
这个想法是将概念扩展到多门网络,将存储在内存数据库中的数组中的值提供给网络的输入,并复制逻辑门网络中的场景评估规则集。假设是Rx允许的流动作应该提供一种机制来及时评估400,000个场景,避免搜索和数组迭代。大多数输入仅仅是前馈,但有些需要使用网络输出,因为下一个方案的决策依赖于前一个方案的输出。
一切正常,直到我尝试在网络中创建反馈循环,试图将网络的输出流用作门的输入流,其输出是网络输出分辨率的一部分
我想知道如何获取输出流并以这种方式使用它。我确实看过"Recursive / fan-out in Reactive Extensions"感觉它可能会有所帮助,但我对Rx很新,并没有完全理解它。
这是我到目前为止的代码。很简单,有点凌乱(道歉)并且目前已经破解(尽管如果反馈尝试被删除并替换为前馈代码,它将起作用。)
class Program
{
private static AndGate _andGate;
private static XorGate _xorGate;
static void Main(string[] args)
{
var data = new List<decimal>();
var data2 = new List<decimal>();
var output = new List<Tuple<Bit, Bit, Bit>>();
var rnd = new Random();
//for (var i = 0; i < 10000; i++)
//{
// data.Add(rnd.Next(0, 100));
// data2.Add(rnd.Next(0, 100));
//}
data.Add(10);
data.Add(30);
data.Add(80);
data.Add(5);
data.Add(34);
data.Add(27);
data2.Add(10);
data2.Add(5);
data2.Add(10);
data2.Add(34);
data2.Add(67);
data2.Add(80);
var stream = data.ToObservable();
var stream2 = data2.ToObservable();
var filter = new DataFilter(stream, a => a > 27);
var filter2 = new DataFilter(stream2, a => a < 12);
_andGate = new AndGate {InputA = filter.OutputStream};
_andGate.InputB = new XorGate(filter2.OutputStream, _andGate.Output.Publish()).Output;
_andGate.SetOutput();
//filter.OutputStream.Subscribe(x => Console.WriteLine(x));
//filter2.OutputStream.Subscribe(x => Console.WriteLine(x));
_andGate.Output.Subscribe(x=>Console.WriteLine(x));
//var zippedSequence =
// Observable.When(
// filter.OutputStream.And(filter2.OutputStream)
// .Then((first, second) => new [] { first, second }));
//zippedSequence.Subscribe(x=>Console.WriteLine("{0}, {1}", x[0], x[1]));
Console.ReadLine();
}
}
public class DataFilter
{
protected IObservable<Bit> ValueStream;
public IObservable<Bit> OutputStream
{
get { return ValueStream; }
}
public DataFilter(IObservable<decimal> stream, Func<decimal, bool> operation )
{
ValueStream = stream.Select(a => operation(a)? Bit.High : Bit.Low);
}
}
public class AndGate
{
protected IObservable<Bit> StreamA;
protected IObservable<Bit> StreamB;
protected IObservable<Bit> OutputStream;
public IObservable<Bit> InputA { get { return StreamA; } set { StreamA = value; } }
public IObservable<Bit> InputB { get { return StreamB; } set { StreamB = value; } }
public IObservable<Bit> Output
{
get { return OutputStream; }
}
public AndGate()
{
OutputStream = Observable.When(StreamA.And(StreamB).Then(Determine));
}
public AndGate(IObservable<Bit> streamA, IObservable<Bit> streamB)
{
OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
}
private Bit Determine(Bit bitA, Bit bitB)
{
return bitA == Bit.High && bitB == Bit.High ? Bit.High : Bit.Low;
}
}
public class OrGate
{
protected IObservable<Bit> InputStreamA;
protected IObservable<Bit> InputStreamB;
protected IObservable<Bit> OutputStream;
public IObservable<Bit> Output
{
get { return OutputStream; }
}
public OrGate()
{
}
public OrGate(IObservable<Bit> streamA, IObservable<Bit> streamB)
{
OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
}
private Bit Determine(Bit bitA, Bit bitB)
{
return bitA == Bit.High || bitB == Bit.High ? Bit.High : Bit.Low;
}
}
public class XorGate
{
protected IObservable<Bit> StreamA;
protected IObservable<Bit> StreamB;
protected IObservable<Bit> OutputStream;
public IObservable<Bit> Output
{
get { return OutputStream; }
}
public XorGate(IObservable<Bit> streamA, IObservable<Bit> streamB)
{
OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
}
private Bit Determine(Bit bitA, Bit bitB)
{
return bitA == Bit.High ^ bitB == Bit.High ? Bit.High : Bit.Low;
}
}
public enum Bit { Low = 0, High = 1}
编辑: 我添加了这个图表以帮助理解。红线是引起问题的线。 ; - )
另一个编辑: 我继续贬低这个小问题并发现了Rx的Generate和Publish / Connect元素,这些元素已经解决了这个问题。通过发布门网络的输出,可以检查输出并基于输出值设置网络元素的变化。 Generate方法允许我设置一个带有两个内部比特流的xor门,1个High和1个Low(最初),这个排列的输出为High。 xor门有一个状态位,可以使用公布的输出流从门外设置。此方法将xor门的输出锁存为High或Low。
class Program
{
private static AndGate _andGate1;
private static AndGate _andGate2;
private static XorGate _xorGate;
static void Main(string[] args)
{
var data = new List<decimal>();
var data2 = new List<decimal>();
var output = new List<Tuple<Bit, Bit, Bit>>();
var rnd = new Random();
//for (var i = 0; i < 10000; i++)
//{
// data.Add(rnd.Next(0, 100));
// data2.Add(rnd.Next(0, 100));
//}
data.Add(10);
data.Add(23);
data.Add(80);
data.Add(5);
data.Add(34);
data.Add(27);
data2.Add(10);
data2.Add(5);
data2.Add(10);
data2.Add(34);
data2.Add(67);
data2.Add(80);
//Raw Data streams
var stream = data.ToObservable();
var stream2 = data2.ToObservable();
//Converted to Bit streams
var filter = new DataFilter(stream, a => a > 27);
var filter2 = new DataFilter(stream2, a => a < 12);
//Gate network
_xorGate = new XorGate();
_andGate1 = new AndGate(filter.OutputStream, _xorGate.Output);
_andGate2 = new AndGate(_andGate1.Output, filter2.OutputStream );
//Publish and Connect to the outcome of the network
var observable = _andGate2.Output.SubscribeOn(NewThreadScheduler.Default).Publish();
observable.Connect();
//Subscribe to the outcome to allow changes to be made to the XorGate
observable.Subscribe(x => { if (x == Bit.High) { _xorGate.SetStatusBitHigh(x); } });
//View the results
observable.Subscribe(x => Console.WriteLine(x));
Console.ReadLine();
}
}
public class DataFilter
{
protected IObservable<Bit> ValueStream;
public IObservable<Bit> OutputStream
{
get { return ValueStream; }
}
public DataFilter(IObservable<decimal> stream, Func<decimal, bool> operation )
{
ValueStream = stream.Select(a => operation(a)? Bit.High : Bit.Low);
}
}
public class AndGate
{
protected IObservable<Bit> OutputStream;
public IObservable<Bit> Output
{
get { return OutputStream; }
}
public AndGate(IObservable<Bit> streamA, IObservable<Bit> streamB)
{
OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
}
private Bit Determine(Bit bitA, Bit bitB)
{
return bitA == Bit.High && bitB == Bit.High ? Bit.High : Bit.Low;
}
}
/// <summary>
/// This is really a bastardized XorGate as I force two Bit streams into the inputs
/// Keep one High, start the other Low and then send it High when a high signal results from the output of the network
/// This allows me to force the network output low i.e. take no action...
/// ...until the Bit is sent low again by another network (no done yet)
/// </summary>
public class XorGate
{
protected IObservable<Bit> StreamA;
protected IObservable<Bit> StreamB;
protected IObservable<Bit> OutputStream;
protected Bit StatusBit;
public void SetStatusBitHigh(Bit input)
{
//No action if circumstances are as you want them
if (input == StatusBit) return;
//Belt and Braces check here
if (StatusBit == Bit.Low && input == Bit.High)
{
StatusBit = Bit.High;
}
}
public void SetStatusBitLow(Bit input)
{
//No action if circumstances are as you want them
if (input == StatusBit) return;
//Belt and Braces check here
if (StatusBit == Bit.High && input == Bit.Low)
{
StatusBit = Bit.Low;
}
}
/// <summary>
/// Output the stream safely
/// </summary>
public IObservable<Bit> Output
{
get { return OutputStream; }
}
public XorGate()
{
//Set the StatusBit to Low initially
StatusBit = Bit.Low;
//Create a permanent high stream for one of the (imaginary) gate pins
var streamA = Observable.Generate(Bit.High, bit => true, bit => bit, bit => bit);
//Create a low stream for the other (imaginary) pin
//One which sets itself to the value of the StatusBit so that a StatusBit = Bit.High will produce a High stream
var streamB = Observable.Generate(Bit.Low, bit => true, bit => StatusBit, bit => bit);
//Produce the output
OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
}
private Bit Determine(Bit bitA, Bit bitB)
{
return bitA == Bit.High ^ bitB == Bit.High ? Bit.High : Bit.Low;
}
}
public class OrGate
{
protected IObservable<Bit> OutputStream;
public IObservable<Bit> Output
{
get { return OutputStream; }
}
public OrGate(IObservable<Bit> streamA, IObservable<Bit> streamB)
{
OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
}
private Bit Determine(Bit bitA, Bit bitB)
{
return bitA == Bit.High || bitB == Bit.High ? Bit.High : Bit.Low;
}
}
public enum Bit { Low = 0, High = 1}
电路现在看起来像这样
我会欢迎评论更正和建议,以其他更好的方式实现这一点,因为它仍然让我觉得有点hacky。
答案 0 :(得分:3)
您需要将系统输出推迟到输入后。这是模拟计算反馈系统的关键部分。
public delegate IObservable<T> Feedback<T>(IObservable<T> feedback, out IObservable<T> output);
public static IObservable<T> FeedbackSystem<T>(Feedback<T> closure)
{
IObservable<T> source = Observable.Empty<T>(), output;
source = closure(Observable.Defer(() => source), out output);
return output;
}
这是一个速度管理器的示例系统,即使存在波动,也能将速度稳定在100。 它根据系统运行速度的反馈调整加速度。 如果您不熟悉运动定律,基本上速度是加速度的结果,加速度由速度反馈控制。
var system =
FeedbackSystem((IObservable<double> acceleration, out IObservable<double> velocity) =>
{
//Time axis: moves forward every 0.1s
double t = 0.1; var timeaxis = Observable.Interval(TimeSpan.FromSeconds(t));
velocity = acceleration.Sample(timeaxis) //move in time
.Scan((u, a) => u + a * t) //u' = u + at
.Select(u => u + new Random().Next(10)) //some variations in speed
.Publish().RefCount(); //avoid recalculation
//negative feedback
var feedback = velocity.Select(u => 0.5 * (100 - u));
return feedback.Select(a => Math.Min(a, 15.0)) //new limited acceleration
.StartWith(0); //initial value
});
system.Subscribe(Console.WriteLine);
反馈通过时间步计算。甚至逻辑门也给出了切换时间。