observables

sdfgrtyu 2018-12-13 11:12:29
连接可观察对象(基于巧合的组合)
组合可观察对象并不仅限于使用发出的元素来创建新类型的结果。另一个有趣的组合是寻找元素之间的关系和逻辑关联——例如,当试图回答哪些元素存在于同一时间框架中时。在查询数据库表或项集合时,连接实体是清楚的——您可以使用对两个或多个实体都通用的值来组合字段。如何将此定义应用于响应流的世界?Rx将共性建立在发生的巧合上,即通知发生在同一时间框架内。简而言之,根据不同观测到的元素在同一时间内存在的巧合,将它们组合起来就是我们所说的联接。您可以通过两种方式连接两个或多个可观察对象。第一种方法是将关节对发射到单个平面流中。第二种方法是创建相关项的组,并将项发出到相关组中。

加入平流
让我们从一个连接可观察对象的例子开始。假设你正在进行一项统计研究,想要得到同时出现在同一房间的男性和女性的通知。这是连接的典型情况,如图9.7所示。要在可观察对象之间创建连接,可以使用Join操作符,它根据重叠的持续时间将两个序列的元素关联起来。Join的签名很复杂,需要一些解释
IObservable<TResult> Join<TLeft, TRight,
TLeftDuration, TRightDuration,TResult>(
IObservable<TLeft>left,
IObservable<TRight> right,
Func<TLeft, IObservable<TLeftDuration>>leftDurationSelector ,
Func<TRight, IObservable<TRightDuration>> rightDurationSelector,
Func<TLeft, TRight, TResult> resultSelector);

方法签名的棘手部分是持续时间选择器函数。这些函数接收发射的元素并返回一个可观察的元素,该元素的发射决定了该元素时间框架的结束。假设您有一个传感器,编码为门事件对象的热观测,它监视进出房间的人。你想让所有的雄性和雌性同时出现在同一个房间里:
IObservable<DoorOpened> doorOpened = doorOpenedSubject.AsObservable();
class DoorOpened
{
public string Name { get; set; }
public OpenDirection Direction { get; set; }
public Gender Gender { get; set; }
}
你可以提取进入房间的雄性和雌性的可观察到的信息,就像这样:
var entrances = doorOpened.Where(o => o.Direction ==
OpenDirection.Entering);
var maleEntering = entrances.Where(x => x.Gender == Gender.Male);
var femaleEntering = entrances.Where(x => x.Gender == Gender.Female);
同样地,你可以提取那些离开的可观察到的
var exits = doorOpened.Where(o => o.Direction == OpenDirection.Leaving);
var maleExiting = exits.Where(x => x.Gender == Gender.Male);
var femaleExiting = exits.Where(x => x.Gender == Gender.Female);
现在,你要把房间里男性的出现和女性的出现联系起来。为此,您需要为每个通知(男性或女性进入)定义标记房间存在的时间框架。对于反应性方法,定义时间框架意味着定义在时间框架关闭时发出(或完成)的可观察对象。下面是如何将其付诸实践
maleEntering
.Join(femaleEntering,
male => maleExiting.Where(exit => exit.Name == male.Name),
female => femaleExiting.Where(exit => female.Name == exit.Name),
(m, f) => new {Male = m.Name, Female = f.Name})
使用查询语法方法,同时查找房间中的男性和女性对看起来是这样的
from male in maleEntering
join female in femaleEntering on maleEntering.Where(exit =>
exit.Name == male.Name) equals
femaleExiting.Where(exit => female.Name == exit.Name)
select new {Male = male.Name, Female = female.Name};
join子句创建一个observable,在该observable上发出所有相关性。 然而,有时候,分而治之的方法更容易使用。 根据这种方法的精神,你希望每个男性都能收到与男性同房的女性所有出现的情况。 因此,每个男性成为所有相关女性群体的群体关键词,并且该群体是这些女性的可观察者。 因此,对于所有对,而不是一个可观察对象,您将拥有多个可观察对象 - 每个对应一个。 对于此行为,您需要使用GroupJoin运算符。
GroupJoin运算符允许您基于重叠持续时间关联两个可观察序列的元素,并将与每个元素相关的元素组合成一个本身就是可观察的组(图9.8)。 例如,在统计观察实验中,您希望为每个男性发射与他在同一房间的所有女性。 你会将每个男性一组的相关女性称为可观察者。 基于巧合,这个群体的动机是,对于每个群组,您可以更容易地定义更精细的查询。 例如,女性群体的平均年龄是多少? GroupJoin运算符具有类似于Join的签名

IObservable<TResult> GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration,
TResult>(
this IObservable<TLeft> left,
IObservable<TRight> right,
Func<TLeft, IObservable<TLeftDuration>> leftDurationSelector,
Func<TRight, IObservable<TRightDuration>> rightDurationSelector,
Func<TLeft, IObservable<TRight>, TResult> resultSelector)
假设您想要扩展上一节中的示例(在一个房间中找到所有成对的男性和女性)。现在,您想要添加一个计数器,该计数器显示到目前为止,每个男性在房间中与之相处的女性数量。 和以前一样,你有可观察到的男性和女性进入和离开房间:
var maleEntering = entrances.Where(x => x.Gender == Gender.Male);
var femaleEntering = entrances.Where(x => x.Gender == Gender.Female);
var maleExiting = exits.Where(x => x.Gender == Gender.Male);
var femaleExiting = exits.Where(x => x.Gender == Gender.Female);
现在,您可以使用GroupJoin创建相关组。 对于每个男性,您创建一个对象,其中包含男性的名字和与他相关的女性观察:
var malesAcquaintances =
maleEntering
.GroupJoin(femaleEntering,
male => maleExiting.Where(exit => exit.Name == male.Name),
female => femaleExiting.Where(exit => female.Name == exit.Name),
(m, females) => new {Male = m.Name, Females = females});
然后,您可以为malesAcquaintances observable创建一个查询,该查询计算每个人在房间中遇到的女性数量并订阅它:
var amountPerUser =
from acquaintances in malesAcquaintances
from cnt in acquaintances.Females.Scan(0, (acc, curr) => acc + 1)
select new {acquaintances.Male, cnt};
输出
Amount of meetings per User - OnNext({ Male = Bob, cnt = 1 })
Amount of meetings per User - OnNext({ Male = John, cnt = 1 })
Amount of meetings per User - OnNext({ Male = Bob, cnt = 2 })
Amount of meetings per User - OnNext({ Male = John, cnt = 2 })
Amount of meetings per User - OnNext({ Male = Dan, cnt = 1 })
查询语法
from male in maleEntering
join female in femaleEntering on maleExiting.Where(e => e.Name == male.Name)
equals femaleExiting.Where(e => female.Name == e.Name)
into maleEncounters
select new { Male = male.Name, Females = maleEncounters}






...全文
150 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
sdfgrtyu 2018-12-13
  • 打赏
  • 举报
回复
sdfgrtyu 2018-12-13
  • 打赏
  • 举报
回复

7,765

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 非技术区
社区管理员
  • 非技术区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧