2013-03-01 3 views
0

Я хотел бы иметь возможность интерпретировать входящие символы и «разбить» их (в данном случае символом пробела).Реализация оператора String Split() в реактивных расширениях

var incomingCharacters = "This is a test".ToCharArray().ToObservable(); 

// Yields a sequence of words - "This", "is", "a", "test" 
var incomingWords = incomingCharacters.Split(' '); 

Я сделал оператора, чтобы сделать это, но мне интересно, есть ли лучший способ?

public static IObservable<string> Split(this IObservable<char> incomingCharacters, char s) 
{ 
    var wordSeq = Observable.Create<string>(observer => 
     { 
      // Create an inner sequence to look for word separators; publish each word to the 
      // "outer sequence" as it is found 
      var innerSeq = incomingCharacters 
       .Concat(Observable.Return(s))   // Enables the last word to be processed 
       .Scan(new StringBuilder(), (builder, c) => 
        { 
         if (c != s) 
         { 
          builder.Append(c); 
          return builder; 
         } 

         // We encountered a "split" character; publish the current completed word 
         // and begin collecting a new one 
         observer.OnNext(builder.ToString()); 
         return new StringBuilder(); 
        }); 

      innerSeq.Subscribe(list => { }); 

      return Disposable.Empty; 
    }); 

    // Return the outer sequence 
    return wordSeq; 
} 
+0

Из-за любопытства, зачем использовать «push-based» Rx здесь? У вас есть полная строка, просто разделите ее на простой LINQ? – JerKimball

+1

Извините, я просто поместил всю строку для облегчения примера - в реальном мире я хотел иметь возможность обрабатывать символы, входящие в один за раз. , , – blaster

ответ

5

Там это способ более простой способ сделать это с помощью Buffer:

public static IObservable<string> Split(
      this IObservable<char> incomingCharacters, 
      char sep) 
{ 
    // Share a single subscription 
    var oneSource = incomingCharacters.Publish().RefCount(); 

    // Our "stop buffering" trigger will be the separators 
    var onlySeparators = oneSource 
     .Where(c => c == sep); 

    return oneSource 
     // buffer until we get a separator 
     .Buffer(onlySeparators) 
     // then return a new string from the buffered chars 
     .Select(carr => new string(carr.ToArray()));   
} 

Тест:

void Main() 
{ 
    var feeder = new Subject<char>(); 
    var query = feeder.Split(' '); 

    using(query.Subscribe(Console.WriteLine)) 
    { 
     foreach(var c in "this should split words on spaces ".ToCharArray()) 
     { 
      feeder.OnNext(c); 
     } 
     Console.ReadLine(); 
    }  
} 

Выход:

this 
should 
split 
words 
on 
spaces 

EDIT: Базовый BufferUntil реали ОЦЕНКА

public static class Ext 
{ 
    public static IObservable<IList<T>> BufferUntil<T>(
     this IObservable<T> source, 
     Func<T, bool> predicate) 
    { 
     var singleSource = source.Publish().RefCount(); 
     var trigger = singleSource.Where(predicate); 
     return singleSource.Buffer(trigger); 
    } 

    public static IObservable<string> Split(
     this IObservable<char> incomingCharacters, 
     char sep) 
    { 
     return incomingCharacters 
      .BufferUntil(c => c == sep) 
      .Select(carr => new string(carr.ToArray())); 
    } 
} 
+0

Важно упомянуть, что это несколько раз подписчивается на 'incomingCharacters', что может вызвать неожиданные побочные эффекты как для горячих, так и для холодных наблюдаемых. Например, напишите свой тест с помощью '', это должно разделить слова на пробелы .ToCharArray(). ToObservable(). Split ('') 'вместо использования' Subject'. – Brandon

+0

@Brandon Ах, дерьмо, я всегда забываю свои публикации ... спасибо, я отредактирую. – JerKimball

+0

:) Иначе это умное решение. Я всегда стараюсь использовать Buffer правильно. – Brandon

Смежные вопросы