Вчера я смотрел скринкаст Написание вашего первого приложения Rx (на канале 9), где Уэс Дайер показывает, как реализовать Drag 'n' Drop с помощью Reactive Extensions (Rx ) . Что-то, чего я до сих пор не понимаю:
В конце экрана Уэс Дайер пишет следующее:
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
. Благодаря природе «1022» «тянуть» его можно повторять снова и снова, всегда получая одни и те же элементы. (По крайней мере это должно иметь место для всех хорошо перечисляемых перечислимых значений.) 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 .: Я только что увидел, что есть еще один скринкаст с оператором Zip
, который довольно проницательный.