【问题标题】:Implementing a String Split() Operator in Reactive Extensions在响应式扩展中实现 String Split() 运算符
【发布时间】:2013-03-05 00:51:44
【问题描述】:

我希望能够解释传入的字符并“拆分”它们(在这种情况下,通过空格字符)。

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;
}

【问题讨论】:

  • 出于好奇,为什么在这里使用“基于推送”的 Rx 选项?你有一个完整的字符串,只需通过普通的 LINQ 拆分它?
  • 对不起,我只是把完整的字符串放进去以方便示例 - 在现实世界中,我希望能够一次处理一个输入的字符。 . .

标签: c# system.reactive


【解决方案1】:

有一种更简单的方法可以使用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 

编辑:一个基本的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()));
    }
}

【讨论】:

  • 值得一提的是,它会多次订阅incomingCharacters,这可能会对冷热可观察对象造成意外的副作用。例如,使用 "this should split words on spaces ".ToCharArray().ToObservable().Split(' ') 而不是 Subject 编写测试。
  • @Brandon 啊,废话,我总是忘记我的发布...谢谢,我会编辑。
  • :) 否则这是一个聪明的解决方案。我一直在努力正确使用 Buffer。
  • @Brandon LeeCampbell 有一篇关于WindowBufferGroupJoin 的精彩博客文章:leecampbell.blogspot.com/2011/03/…
  • 哇,非常感谢!我的心有点炸了。这让我希望 Buffer() 方法有一个重载,可以直接接收布尔过滤器(在这种情况下,类似于 .BufferUntil(c => c == sep))。这让我看到了使用 Buffer 方法可以做什么。再次感谢!
猜你喜欢
  • 2021-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-14
  • 1970-01-01
相关资源
最近更新 更多