2010-09-03 66 views
2

昨天我看了Screencast Writing your first Rx Application(在频道9),Wes Dyer展示了如何使用Reactive Extensions (Rx)实现Drag'n'Drop。东西,我还是不明白:Rx:可观测量是否像IEnumerable那样“可重复”,如果不是,这个代码是如何工作的?

迎截屏结束,韦斯代尔类型如下:

var q = from start in mouseDown 
     from delta in mouseMove.StartsWith(start).Until(mouseUp) 
         .Let(mm => mm.Zip(mm.Skip(1), (prev, cur) => 
          new { X = cur.X - prev.X, Y = cur.Y - prev.Y })) 
     select delta; 

简而言之,q是可观察到的是推动了鼠标移动的坐标增量向其用户。

我不明白的是如何mm.Zip(mm.Skip(1), ...)可能工作!?

据我所知,IObservable是不可枚举的,因为IEnumerable是。由于IEnumerable的“拉”性质,它可以反复迭代,总是产生相同的项目。 (至少应该是就是所有乖巧枚举的情况。)IObservable的工作方式不同。项目被推送给订阅者一次,就是这样。在上面的例子中,鼠标移动是单个事件,如果没有记录在内存中就不能重复。

那么,.Zip.Skip(1)的组合如何工作,因为他们正在处理的鼠标事件是单一的,不可重复的事件?此操作是否要求mm两次独立“查看”?


仅供参考,这里有Observable.Zip方法签名:

public static IObservable<TResult> Zip <TLeft, TRight, TResult> 
(
    this IObservable<TLeft>  leftSource,  // = mm 
    IObservable<TRight>   rightSource, // = mm.Skip(1) 
    Func<TLeft, TRight, TResult> selector 
) 

P.S:我刚才看到有另一个screencast on the Zip operator这是相当有见地。

回答

3

不这样操作要求毫米“看着”独立两次?

这就是你的问题的答案:你可以多次订阅相同的IObservable序列。

mm.Skip(1)订阅mm并将第一个值隐藏给自己的订户。 Zip是mm.Skip(1)mm的用户。由于mm产生了比mm.Skip(1)多一个值,因此Zip会始终缓存来自mm的最后一个mousemove事件,以便在未来的未来mousemove事件中对其进行压缩。选择器功能可以选择两者之间的增量。

你应该注意到的另一件事是(这是真正的回答你的问题的标题),这Observable.FromEvent - IObservable观察到的,因此不可重复。但有 Observable其实可重复,像Observable.Range(0,10)。在后一种情况下,每个用户将收到相同的10个事件,因为它们是为每个用户独立生成的。对于mousemove事件,情况并非如此(您不会从过去获得鼠标移动事件)。但是因为Zip在这种情况下同时订阅了左右序列。

P.S .:你也可以创建一个热/不可重复的IEnumerable:它不需要为每个枚举器返回相同的值。你可以创建一个IEnumerable,它会一直等到mousemove事件发生,然后产生事件。在这种情况下,枚举器总是会阻塞(坏设计),但这是可能的。 ;)

+0

_ @ Nappy_,感谢您解释“热”与“冷”的术语位。当我问这个问题时,我缺乏恰当的词汇,这让事情变得更加清晰。 – stakx 2010-09-04 21:36:29

1

项目被推送给订户一次,就是这样。

是的,一个项目被推送一次,但该项目是事件的“序列”之一。该序列仍然是一个序列。这就是Skip的原理 - 它跳过一个项目,然后当下一个项目到来时,处理它(不跳过它)。

2

啊哈!我在P.S.中提到的Zip screencast。给了我一个至关重要的线索:Zip“记得”了物品,以说明物品可以从一个可观察物体到达而不是从另一个物体到达。我会试着回答我的问题,我希望有人能纠正我,如果我错了。从这样的两个可观察序列

Zip对了输入(字母和数字组成的“事件”):

mm      ----A---------B-------C------D-----------E-----> 
           |   |  |  |   | 
           |   |  |  |   | 
mm.Skip(1)    ----+---------1-------2------3-----------4-----> 
           |   |  |  |   | 
           |   |  |  |   | 
mm.Zip(mm.Skip(1), ...) ----+--------A,1-----B,2----C,3---------D,4----> 

而且它确实做到内部缓冲。在我发布的代码中,mm是真正的“活”可观察的。 mm.Skip(1)就像是从它派生出来的状态机。亚历克斯帕文的答案简要解释了这是如何工作的。

因此,mm.Zip(mm.Skip(1), ...)确实看过mm两次,一次是直接,一次是通过Skip(n)过滤器。而且因为可观测量不是可重复的序列,所以它会进行内部缓冲来说明这样一个事实,即一个序列会比另一个序列产生更快的项目。

(我迅速扫了一眼在Rx源与.net反射而事实上,Zip涉及Queue

+1

请参阅这里的大理石图:http://wiki.github.com/richardszalay/rxas/zip – 2010-09-13 12:48:19

相关问题