代码之家  ›  专栏  ›  技术社区  ›  SuperJMN

组合依赖于其他可观察物的可观察物

  •  1
  • SuperJMN  · 技术社区  · 7 年前

    1. 高度从0增加到 BaseAltitude ,这是一个固定的高度。
    2. 基准高度 到达后,无人机开始巡航,描述一个正弦波,从
    3. 收到信号后,无人机应该开始降落。这是,从 现在的 .

    正如您可能注意到的,当着陆开始时,高度在设计时是未知的。起飞顺序应以最后一个高度为起点。因此,一个序列依赖于另一个序列产生的最后一个值。我的脑子疼!

    好吧,我完全被这个困住了。

    我目前仅有的代码如下。我用它来说明这个问题。你会很快得到的。。。

    public class Drone
    {
        public Drone()
        {
            var interval = TimeSpan.FromMilliseconds(200);
    
            var takeOff = Observable.Interval(interval).TakeWhile(h => h < BaseAltitude).Select(t => (double)t);
    
            var cruise = Observable
                .Interval(interval).Select(t => 100 * Math.Sin(t * 2 * Math.PI / 180) + BaseAltitude)
                .TakeUntil(_ => IsLanding);
    
            var landing = Observable
                .Interval(interval).Select(t => ??? );
    
            Altitude = takeOff.Concat(cruise).Concat(landing);
        }
    
        public bool IsLanding { get; set; }
        public double BaseAltitude { get; set; } = 100;
        public IObservable<double> Altitude { get; }
    }
    
    2 回复  |  直到 7 年前
        1
  •  3
  •   Enigmativity    7 年前

    你真的应该尝试制作可观察的模型,这样它就可以模拟起飞或着陆

    public class Drone
    {
        public Drone()
        {
            this.Altitude = ...
        }
    
        private bool _isLanding = true;
        private Subject<bool> _isLandings = new Subject<bool>();
    
        public bool IsLanding
        {
            get => _isLanding;
            set
            {
                _isLanding = value;
                _isLandings.OnNext(value);
            }
        }
    
        public double BaseAltitude { get; set; } = 100.0;
        public IObservable<double> Altitude { get; }
    }
    

    IsLanding _isLandings 激发可用于更改无人机模式的值。

    现在,定义 Altitude 从以下基本模式开始:

        this.Altitude =
            _isLandings
                .Select(x => x ? landing : takeOff.Concat(cruise))
                .Switch()
                .StartWith(altitude);
    

    使用 .Switch() _孤岛 生成一个值,开关在着陆或起飞之间进行选择。它变成了一个单一的可观察物,对上升或下降作出反应。

    完整代码如下所示:

    public class Drone
    {
        public Drone()
        {
            var altitude = 0.0;
            var interval = TimeSpan.FromMilliseconds(200);
    
            IObservable<double> landing =
                Observable
                    .Interval(interval)
                    .TakeWhile(h => altitude > 0.0)
                    .Select(t =>
                    {
                        altitude -= 10.0;
                        altitude = altitude > 0.0 ? altitude : 0.0;
                        return altitude;
                    });
    
            IObservable<double> takeOff =
                Observable
                    .Interval(interval)
                    .TakeWhile(h => altitude < BaseAltitude)
                    .Select(t =>
                    {
                        altitude += 10.0;
                        altitude = altitude < BaseAltitude ? altitude : BaseAltitude;
                        return altitude;
                    });
    
            IObservable<double> cruise =
                Observable
                    .Interval(interval)
                    .Select(t =>
                    {
                        altitude = 10.0 * Math.Sin(t * 2.0 * Math.PI / 180.0) + BaseAltitude;
                        return altitude;
                    });
    
            this.Altitude =
                _isLandings
                    .Select(x => x ? landing : takeOff.Concat(cruise))
                    .Switch()
                    .StartWith(altitude);
        }
    
        private bool _isLanding = true;
        private Subject<bool> _isLandings = new Subject<bool>();
    
        public bool IsLanding
        {
            get => _isLanding;
            set
            {
                _isLanding = value;
                _isLandings.OnNext(value);
            }
        }
    
        public double BaseAltitude { get; set; } = 100.0;
        public IObservable<double> Altitude { get; }
    }
    

    var drone = new Drone();
    
    drone.Altitude.Subscribe(x => Console.WriteLine(x));
    
    Thread.Sleep(2000);
    
    drone.IsLanding = false;
    
    Thread.Sleep(4000);
    
    drone.IsLanding = true;
    
        2
  •  3
  •   Shlomo    7 年前

    你用 LastAsync 获取的最后一个值 cruise ,那么 SelectMany 进入你想要的可观察的世界。

    你需要换衣服 巡航

        var cruise = Observable.Interval(interval)
            .Select(t => 100 * Math.Sin(t * 2 * Math.PI / 180) + BaseAltitude)
            .TakeUntil(_ => IsLanding)
            .Replay(1)
            .RefCount();
    
        var landing = cruise
            .LastAsync()
            .SelectMany(maxAlt => Observable.Interval(interval).Select(i => maxAlt - i))
            .TakeWhile(alt => alt >= 0);
    
        Altitude = takeOff.Concat(cruise).Concat(landing);
    

    为什么我需要 .Replay(1).Refcount() ?

    这里的一切都是冷可观察的,它们都不会同时运行。 Concat

    t        : 1-2-3-4-5-6-7-8-9-0-1-2-3-4-5-6-7-8-...
    takeOff  : 1-2-3-4-5-|
    cruise   :           6-7-8-7-6-|
    isLanding: T-------------------F----------------
    landing  :                     5-4-3-2-1-0-|
    

    如果你定义 landing = cruise.LastAsync()... 巡航 在时间11得到最后一个值。

    • 巡航 按照您的定义,它将尝试重新订阅一个新的cold observate,这将导致0个元素,因为 isLanding
    • 如果你加上 .Publish().RefCount() 巡航
    • .Replay(1).Refcount()