积累和流重置价值 [英] Accumulating and resetting values in a stream

查看:121
本文介绍了积累和流重置价值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在玩无编程,使用RxJS和偶然的东西我不知道如何解决。

比方说,我们实现一个自动售货机。您插入一枚硬币,选择项目,并且该机免除项目并返回改变。我们假设价格永远是1分钱,所以插入四分之一(25美分)应返回24美分回来,等等。

在刁钻的部分是,我希望能够处理情况下,像用户插入2硬币,然后选择一个项目。或选择项目不插入一枚硬币。

他很自然地实现插入硬币和所选项目的数据流。然后,我们可以介绍一些这两个动作之间的依赖 - 合并或拉拉链或结合最新的

不过,我赶紧跑到哪里,我想硬币累积,直到一个项目被分配但未进一步的问题。 AFAIU,这意味着因为没有办法复位previous我不能使用扫描堆积在某个时候。

下面是一个例子图:

 硬币:--- 25 --- 5 ----- 10 ------------ |  - >
ACC:--- 25 --- 30 ---- 40 ------------ | - >
项目:------------富----- -----酒吧| - >
联合:--------- 30,富 - 40,bar-- | - >
改变:------------ 29 ------ 39 ------ | - >

和相应的code:

  this.getCoinsStream()
  .scan(功能(总和,电流){返回总和+当前})
  .combineLatest(this.getSelectedItemsStream())
  .subscribe(函数(仙,项){
    dispenseItem(项目);
    dispenseChange(美分 - 1);
  });

25和5美分插入,然后选择foo的项目。积累硬币,然后结合最新的会导致富正与30(这是正确的),然后在酒吧和40组合(这是不正确;应该是酒吧和10)

我通过看着所有的方法进行分组和筛选,并没有看到任何东西,我可以使用。

另一种解决方案,我可以用的是单独积累硬币。但这导致状态的数据流之外,我真的想避免:

  VAR centsDeposited = 0;
this.getCoinsStream()。认购(功能(美分){
  返回centsDeposited + =美分;
});this.getSelectedItemsStream()。认购(函数(项目){
  dispenseItem(项目);
  dispenseChange(centsDeposited - 1);
  centsDeposited = 0;
});

此外,这不允许用于使流依赖于彼此,如等待硬币被插入,直到选择的动作可以返回一个项目

我失去了现有的方法?什么是实现这样的事情的最好方法? - 积累值,直到那一刻,当他们需要与另一个流合并,但是从第2个一合并之前还等着在第一次流至少值1


解决方案

您可以使用您的扫描/ combineLatest方法,然后用第一跟进一个完成流重复,使其重新开始流,但你的观察员将无法看到它。

\r
\r

VAR coinStream = Rx.Observable.merge(\r
    Rx.Observable.fromEvent($('#ADD5'),'点击')。图(5)\r
    Rx.Observable.fromEvent($('#ADD10'),'点击')。图(10)\r
    Rx.Observable.fromEvent($('#ADD25'),'点击')。图(25)\r
    );\r
VAR selectedStream = Rx.Observable.merge(\r
  Rx.Observable.fromEvent($('#焦炉'),'点击')。图('可乐'),\r
  Rx.Observable.fromEvent($('#雪碧'),'点击')。图('雪碧')\r
);\r
\r
变量$选择= $('#选择');\r
变量$ =变动$('#改变');\r
\r
功能分配(选择){\r
 $ selection.text(配发的:+选择);\r
 的console.log(配镜饮料:+选择);\r
}\r
\r
功能dispenseChange(其他城市){\r
 $ change.text(配发的变化:+变化);\r
 的console.log(赎+变化);\r
}\r
\r
\r
VAR饮水机= coinStream.scan(功能(ACC,三角洲){返回ACC +三角洲;},0)\r
                              .combineLatest(selectedStream,\r
                                 功能(硬币,选择){\r
                                   返回{金币:金币,选择:选择};\r
                                 })\r
\r
                              //结合最新不会发出直到两个观测量有一个值\r
                              //所以你可以放心地拿到第一,这将是点\r
                              //这两个观测量已经发出。\r
                              。第一()\r
\r
                              //首先将完成上面流所以使用重复\r
                              //重新订阅流透明\r
                              //你也可以使用whil​​e或doWhile做到这一点有条件\r
                              。重复()\r
\r
                              //如果你只是将认购一次,那么你就不需要这个,但\r
                              //我在这里展示如何使用两个用户做\r
                              。发布();\r
\r
    \r
    //施舍的变化\r
    dispenser.pluck('硬币')\r
             .MAP(功能(C){回复C - 1;})\r
             .subscribe(dispenseChange);\r
    \r
    //获取豁免的选择\r
    dispenser.pluck('选择')认购(分配)。\r
    \r
    //丝起来\r
    dispenser.connect();

\r

&LT;脚本SRC =htt​​ps://ajax.googleapis.com/ajax /libs/jquery/2.1.1/jquery.min.js\"></script>\r
&所述; SCRIPT SRC =htt​​ps://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js&GT;&下; /脚本&GT;\r
&LT;按钮ID =可乐&GT;焦炭及LT; /按钮&GT;\r
&LT;按钮ID =雪碧&GT;雪碧&LT; /按钮&GT;\r
&LT;按钮ID =ADD5&GT; 5℃/按钮&GT;\r
&LT;按钮ID =ADD10大于10&LT; /按钮&GT;\r
&LT;按钮ID =ADD25&GT; 25℃/按钮&GT;\r
&LT; D​​IV ID =变与GT;&LT; / DIV&GT;\r
&LT; D​​IV ID =选择&GT;&LT; / DIV&GT;

\r

\r
\r

I'm playing with Reactive Programming, using RxJS, and stumbled upon something I'm not sure how to solve.

Let's say we implement a vending machine. You insert a coin, select an item, and the machine dispenses an item and returns change. We'll assume that price is always 1 cent, so inserting a quarter (25 cents) should return 24 cents back, and so on.

The "tricky" part is that I'd like to be able to handle cases like user inserting 2 coins and then selecting an item. Or selecting an item without inserting a coin.

It seems natural to implement inserted coins and selected items as streams. We can then introduce some sort of dependency between these 2 actions — merging or zipping or combining latest.

However, I quickly ran into an issue where I'd like coins to be accumulated up until an item is dispensed but not further. AFAIU, this means I can't use sum or scan since there's no way to "reset" previous accumulation at some point.

Here's an example diagram:

coins: ---25---5-----10------------|->
acc:   ---25---30----40------------|->
items: ------------foo-----bar-----|->
combined: ---------30,foo--40,bar--|->
change:------------29------39------|->

And a corresponding code:

this.getCoinsStream()
  .scan(function(sum, current) { return sum + current })
  .combineLatest(this.getSelectedItemsStream())
  .subscribe(function(cents, item) {
    dispenseItem(item);
    dispenseChange(cents - 1);
  });

25 and 5 cents were inserted and then "foo" item was selected. Accumulating coins and then combining latest would lead to "foo" being combined with "30" (which is correct) and then "bar" with "40" (which is incorrect; should be "bar" and "10").

I looked through all of the methods for grouping and filtering and don't see anything that I can use.

An alternative solution I could use is to accumulate coins separately. But this introduces state outside of a stream and I'd really like to avoid that:

var centsDeposited = 0;
this.getCoinsStream().subscribe(function(cents) {
  return centsDeposited += cents;
});

this.getSelectedItemsStream().subscribe(function(item) {
  dispenseItem(item);
  dispenseChange(centsDeposited - 1);
  centsDeposited = 0;
});

Moreover, this doesn't allow for making streams dependent on each other, such as to wait for coin to be inserted until selected action can return an item.

Am I missing already existing method? What's the best way to achieve something like this — accumulating values up until the moment when they need to be merged with another stream, but also waiting for at least 1 value in 1st stream before merging it with the one from the 2nd?

解决方案

You could use your scan/combineLatest approach and then finish the stream with a first followed up with a repeat so that it "starts over" the stream but your Observers would not see it.

var coinStream = Rx.Observable.merge(
    Rx.Observable.fromEvent($('#add5'), 'click').map(5),
    Rx.Observable.fromEvent($('#add10'), 'click').map(10),
    Rx.Observable.fromEvent($('#add25'), 'click').map(25)
    );
var selectedStream = Rx.Observable.merge(
  Rx.Observable.fromEvent($('#coke'), 'click').map('Coke'),
  Rx.Observable.fromEvent($('#sprite'), 'click').map('sprite')
);

var $selection = $('#selection');
var $change = $('#change');

function dispense(selection) {
 $selection.text('Dispensed: ' + selection); 
 console.log("Dispensing Drink: " + selection);   
}

function dispenseChange(change) {
 $change.text('Dispensed change: ' + change); 
 console.log("Dispensing Change: " + change);   
}


var dispenser = coinStream.scan(function(acc, delta) { return acc + delta; }, 0)
                              .combineLatest(selectedStream, 
                                 function(coins, selection) {
                                   return {coins : coins, selection : selection};
                                 })

                              //Combine latest won't emit until both Observables have a value
                              //so you can safely get the first which will be the point that
                              //both Observables have emitted.
                              .first()

                              //First will complete the stream above so use repeat
                              //to resubscribe to the stream transparently
                              //You could also do this conditionally with while or doWhile
                              .repeat()

                              //If you only will subscribe once, then you won't need this but 
                              //here I am showing how to do it with two subscribers
                              .publish();

    
    //Dole out the change
    dispenser.pluck('coins')
             .map(function(c) { return c - 1;})
             .subscribe(dispenseChange);
    
    //Get the selection for dispensation
    dispenser.pluck('selection').subscribe(dispense);
    
    //Wire it up
    dispenser.connect();

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>
<button id="coke">Coke</button>
<button id="sprite">Sprite</button>
<button id="add5">5</button>
<button id="add10">10</button>
<button id="add25">25</button>
<div id="change"></div>
<div id="selection"></div>

这篇关于积累和流重置价值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆