Короткий ответ заключается в том, что Rx Framework не поддерживает создание наблюдаемых с использованием такого рекурсивного шаблона, поэтому это нелегко сделать. Операция Combine
, используемая для последовательностей F#, требует специальной обработки, которую не обеспечивают наблюдаемые объекты. Rx Framework, вероятно, ожидает, что вы будете генерировать наблюдаемые объекты с помощью Observable.Generate
, а затем использовать для их обработки запросы LINQ/построитель вычислений F#.
Тем не менее, вот некоторые мысли -
Прежде всего, вам нужно заменить Observable.merge
на Observable.Concat
. Первый запускает обе наблюдаемые параллельно, а второй сначала выдает все значения из первой наблюдаемой, а затем создает значения из второй наблюдаемой. После этого изменения фрагмент будет печатать как минимум ~800 чисел до переполнения стека.
Причина переполнения стека заключается в том, что Concat
создает наблюдаемую, которая вызывает Concat
, чтобы создать другую наблюдаемую, которая вызывает Concat
и т. д. Один из способов решить эту проблему — добавить некоторую синхронизацию. Если вы используете Windows Forms, вы можете изменить Delay
так, чтобы он планировал наблюдаемое в потоке графического интерфейса (который отбрасывает текущий стек). Вот набросок:
type RxBuilder() =
member this.Delay f =
let sync = System.Threading.SynchronizationContext.Current
let res = Observable.Defer f
{ new IObservable<_> with
member x.Subscribe(a) =
sync.Post( (fun _ -> res.Subscribe(a) |> ignore), null)
// Note: This is wrong, but we cannot easily get the IDisposable here
null }
member this.Combine (xs, ys) = Observable.Concat(xs, ys)
member this.Yield x = Observable.Return x
member this.YieldFrom (xs:IObservable<_>) = xs
Чтобы реализовать это должным образом, вам пришлось бы написать свой собственный метод Concat
, что довольно сложно. Идея будет заключаться в следующем:
- Concat возвращает какой-то специальный тип, например.
IConcatenatedObservable
- Когда метод вызывается рекурсивно, вы создаете цепочку из
IConcatenatedObservable
, которые ссылаются друг на друга.
- Метод
Concat
будет искать эту цепочку, и когда есть, например. три объекта, он бросит средний (чтобы всегда сохранять цепочку длиной не более 2).
Это слишком сложно для ответа StackOverflow, но это может быть полезным отзывом для команды Rx.
person
Tomas Petricek
schedule
28.05.2011
Expand
в последних сборках Rx? Он обрабатывает определенные сценарии рекурсии. - person Richard Szalay   schedule 29.05.2011