MR. Dev

Blog programisty.

Programowanie Reaktywne - Tworzymy dane - Generators.


Artykuł ten jest częścią serii artykułów na temat Programowania reaktywnego.

Zapraszam na GitHub-a.

Tematy

  1. Wstęp
  2. Zabawa z czasem - Timer
  3. Kto za tym stoi? - Scheduler
  4. Nie zapominaj - Subscribe
  5. Zabawa z czasem - Interval
  6. Zabawa z czasem - Buffer
  7. Zabawa z czasem - Delay
  8. Zabawa z czasem - Sample
  9. Zabawa z czasem - Throttle
  10. Zabawa z czasem - Timestamp/TimeInterval
  11. Tworzymy dane - Generators
  12. Tworzymy dane - Własna klasa publikująca
  13. Marudzimy - Skip
  14. Marudzimy - Take
  15. Łap To! - ConsoleKey
  16. Kombinatorzy - Concat
  17. Kombinatorzy - Repeat
  18. Kombinatorzy - Start With
  19. Kombinatorzy - Ambiguous
  20. Kombinatorzy - Merge
  21. Kombinatorzy - Zip
  22. Kombinatorzy - Switch
  23. Kombinatorzy - When-And-Then
  24. Kombinatorzy - Combine Latest
  25. Transformers - Select
  26. Transformers - OfType and Cast
  27. Transformers - Metadata
  28. Bileciki do kontroli - Unit Tests of Interval
  29. Bileciki do kontroli - Unit Tests of Observer Interval
  30. Bileciki do kontroli - Unit Tests of Create Cold/Hot Observable
  31. Szpryca - AutoFac

Wstęp

Reactive Extensions - Range Dzisiaj poznamy coś nowego, jednak skorzystamy z czegoś starego. Jako inicjatory generowania danych będziemy korzystać jeszcze wielokrotnie z Timer-ów. Bardzo dobrze sprawdzają się jako taki niewolnik, który będzie robił co mu karzemy. “Daj mi tyle i tyle co tyle”.

Observable.Range

Bardzo prostym przykładem generowania danych jest Range. Dlatego nieco go skomplikujemy:). Wygenerujemy prostą tabelką zawierającą informacje o Timestamp-ie, Interval-le i generowanej wartości. Jak można uzyskać taki efekt? Opakowując strumień danych 2x => .TimeInterval().Timestamp(). Skutkuje to otrzymywaniem danych z obiektu obserwowanego w postaci:

  • Timestamp,
  • Value.Interval,
  • Value.Value.

Value to pierwsze opakowanie przy pomocy .TimeInterval().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Console.WriteLine("RangeGenerator");
Console.WriteLine("Timestamp\t\t\tInterval\t\tValue");

var rangeSource = Observable.Range(start, count)
	.TimeInterval()
	.Timestamp();

_rangeSubscription = rangeSource.Subscribe(
	item =>
	{
		Console.WriteLine("{0}\t{1}\t{2}", 
			item.Timestamp, 
			item.Value.Interval, 
			item.Value.Value);
	},

A co nam daje ten Range? To proste, określamy wartość początkową (start) oraz ilość (count) danych jakie będziemy generować: Observable.Range(start, count).

1
var rangeGenerator = new RangeGenerator(-100, 200);

Prosty przykład:

Dane: start = 0, count = 10

Wynik: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

Observable.Generate

Reactive Extensions - Generate Jest i kolejny sposób generowania danych, bardziej wyszukany. Pozwala na kilka ciekawych zabiegów:

  • initialState - wartość początkowa,
  • condition - warunki jakie możemy określić, jeżeli ich wynikiem będzie true to wówczas będą generowane kolejne dane,
  • iterate - jak w pętli for, należy modyfikować podstawę warunków, tak by generowanie mogło się zakończyć, jest to też podstawa generowanych danych,
  • resultSelector - wynik iteracji możemy jeszcze zmodyfikować i tutaj można to zrobić, przykład poniżej (Factorial),
  • timeSelector - jeszcze trzeba zainicjować proces samodzielnego generowania danych, w tym celu ustalamy co ile czasu będzie wykonywany kolejny etap generowania danych.

Można porównać ten operator do reaktywnej pętli for:).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static ulong Factorial(ulong i) => i < 1 ? 1 : i * Factorial(i - 1);

public FactorialGenerator()
{
	Console.WriteLine("Factorial");

	var initialState = 1ul;

	var observable = Observable.Generate(
		initialState,
		condition => condition < 23,
		iterate => iterate + 1,
		resultSelector => Factorial(resultSelector),
		timeSelector => TimeSpan.FromMilliseconds(100)
		).TimeInterval();

	_subscribeGenerator = observable.Subscribe(
		item =>
		{
			Console.WriteLine("{0} -> {1}", item.Interval, item.Value);
		},

Factorial to nic innego jak silnia, zapisana w bardzo skróconej wersji z wykorzystaniem rekurencji.

Z rekurencją trzeba uważać. To żyje własnym życiem! Samo się żywi! Rozmnaża! I czasem nie chce zginąć!

Wyniki generowania przy udziale Silni można zaobserwować oczywiście w przykładzie na GitHub-ie.

1
var generator = new FactorialGenerator();

Zakończenie

Jak widać w powyższych moich wypocinach. Możemy wygenerować interesujące dane asynchronicznie oraz rozesłać je po najdalszych krańcach programu. A z krańców do centrum przy wykorzystaniu innych reaktywnych mechanizmów. Dalej przetworzyć dystrybuować do kolejnych zainteresowanych. Plotkowanie na całego…


Jest to post wchodzący w skład podjętego wyzwania ogłoszonego przez MIROBURN we vlogu z dnia 3 lutego 2018 roku.

Celem wyzwania jest systematyczne działanie w ciągu 30 dni.

Postanowiłem pisać post dziennie o tematyce Programowania Reaktywnego dla platformy .NET.

Wszelkie źródła związane z postami znajdują się na repozytorium GitHub.

Stan obecny wyzwania: 30 z 30 dni.


Referencje:


Wcześniejszy: Programowanie Reaktywne - Zabawa z czasem - Timestamp/TimeInterval

Następny: Programowanie Reaktywne - Tworzymy dane - Własna klasa publikująca