2013-03-06 18 views
7

में एक स्लाइडिंग विंडो को कार्यान्वित करने में समस्या मैंने प्रतिक्रियाशील एक्सटेंशन के लिए एक स्लाइडिंगविंडो() ऑपरेटर बनाया क्योंकि मैं आसानी से रोलिंग औसत जैसी चीज़ों की निगरानी करना चाहता हूं। एक साधारण उदाहरण के रूप में, मैं माउस ईवेंट सुनने के लिए सदस्यता लेना चाहता हूं, लेकिन हर बार एक ऐसा कार्यक्रम है जिसे मैं अंतिम तीन प्राप्त करना चाहता हूं (पिछले तीन प्राप्त करने के लिए हर तीसरे कार्यक्रम की प्रतीक्षा करने के बजाय)। यही कारण है कि मुझे मिली विंडो ओवरलोड मुझे ऐसा नहीं लगता है कि मुझे बॉक्स से बाहर क्या चाहिए।आरएक्स

यही वह है जो मैं आया था। मुझे डर है कि यह सबसे शक्तिशाली समाधान नहीं हो सकता है इसकी लगातार सूची संचालन दिया:

public static IObservable<List<T>> SlidingWindow<T>(this IObservable<T> seq, int length) 
{ 
    var seed = new List<T>(); 

    Func<List<T>, T, List<T>> accumulator = (list, arg2) => 
    { 
     list.Add(arg2); 

     if (list.Count > length) 
      list.RemoveRange(0, (list.Count - length)); 

     return list; 
    }; 

    return seq.Scan(seed, accumulator) 
       .Where(list => list.Count == length); 
} 

यह इस तरह से कहा जाता जा सकता है:

var rollingSequence = Observable.Range(1, 5).SlidingWindow().ToEnumerable(); 

लेकिन, मेरा महान आश्चर्य करने के लिए, बजाय प्राप्त करने के अपेक्षित परिणाम

1,2,3 
2,3,4 
3,4,5 

मैं परिणाम प्राप्त

2,3,4 
3,4,5 
3,4,5 

किसी भी अंतर्दृष्टि की सराहना की जाएगी!

उत्तर

5

इस बजाय का प्रयास करें - मैं बैठते हैं और एक थिंक के बारे में यह रिश्तेदार प्रदर्शन है करना चाहते हैं, लेकिन यह कम से कम है संभावना के रूप में अच्छा है, और जिस तरह से पढ़ने में आसान:

public static IObservable<IList<T>> SlidingWindow<T>(
     this IObservable<T> src, 
     int windowSize) 
{ 
    var feed = src.Publish().RefCount();  
    // (skip 0) + (skip 1) + (skip 2) + ... + (skip nth) => return as list 
    return Observable.Zip(
     Enumerable.Range(0, windowSize) 
      .Select(skip => feed.Skip(skip)) 
      .ToArray()); 
} 

टेस्ट रिग :

var source = Observable.Range(0, 10); 
var query = source.SlidingWindow(3); 
using(query.Subscribe(Console.WriteLine)) 
{    
    Console.ReadLine(); 
} 

आउटपुट:

ListOf(0,1,2) 
ListOf(1,2,3) 
ListOf(2,3,4) 
ListOf(3,4,5) 
ListOf(4,5,6) 
... 

संपादित करें: एक तरफ के रूप में, मैं खुद को अनिवार्य रूप से .Publish().RefCount() आईएनजी कभी नहीं मिला क्योंकि इसे एक बार जला दिया जा रहा है ... मुझे नहीं लगता कि यह कड़ाई से यहां जरूरी है, वैसे। yzorg के लिए

संपादित करें:

तुम इतनी तरह विधि बढ़ाने हैं, तो आप क्रम व्यवहार अधिक स्पष्ट रूप से देख सकते हैं:

public static IObservable<IList<T>> SlidingWindow<T>(
    this IObservable<T> src, 
    int windowSize) 
{ 
    var feed = src.Publish().RefCount();  
    // (skip 0) + (skip 1) + (skip 2) + ... + (skip nth) => return as list 
    return Observable.Zip(
    Enumerable.Range(0, windowSize) 
     .Select(skip => 
     { 
      Console.WriteLine("Skipping {0} els", skip); 
      return feed.Skip(skip); 
     }) 
     .ToArray()); 
} 
+0

@blaster कोई समस्या नहीं - असल में, मुझे "बनाने" के लिए धन्यवाद, इसे लिखने के लिए धन्यवाद, क्योंकि मैंने इसे जवाब देने के बाद इसे दो बार इस्तेमाल किया है। ;) – JerKimball

+0

मुझे नहीं लगता कि यह अच्छा है। Publish(), .Range (0, x) और .Skip() - जब ये संयुक्त होते हैं तो यह खराब प्रदर्शन की तरह दिखता है, विशेष रूप से ओ n^2, क्योंकि Skip पूरे स्ट्रीम को फिर से चालू करने जा रहा है।उदाहरण के लिए, आपको (10000, 10001, 10002) प्राप्त करने के लिए 30,000 पूर्णांक फिर से शुरू करने की आवश्यकता होगी। तो आप वास्तव में स्मृति में स्रोत स्ट्रीम का एक स्लाइडिंग बफर नहीं रखते हैं, आपको स्मृति में संपूर्ण स्रोत स्ट्रीम (समय की शुरुआत के बाद से) रखना होगा, जो मैंने सोचा था कि हम से परहेज कर रहे थे। – yzorg

+0

@yzorg संपादित करें – JerKimball

9

, गिनती के लिए 3 के एक तर्क के साथ अपने मूल परीक्षण का उपयोग करना,

public static IObservable<IList<T>> SlidingWindow<T>(
    this IObservable<T> source, int count) 
{ 
    return source.Buffer(count, 1) 
       .Where(list => list.Count == count); 
} 

परीक्षण इस तरह:: इस वांछित परिणाम देता है

var source = Observable.Range(1, 5); 
var query = source.SlidingWindow(3); 
using (query.Subscribe(i => Console.WriteLine(string.Join(",", i)))) 
{ 

} 

आउटपुट:

1,2,3 
2,3,4 
3,4,5 
6

बस source.Window(count, 1) - या source.Buffer(count, 1) यह "गिनती" मदों की एक खिड़की/बफर, एक के बाद रपट हो।

संबंधित मुद्दे