MR. Dev

Blog programisty.

Programowanie Reaktywne - Kombinatorzy - Zip.


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 - Zip Kontynuując tematykę kombinatorów. Dzisiaj o pewnym znanym słówku, bardzo popularnym formacie kompresji danych. Nie wiem jakim cudem trafił do Rx-owej rodziny. Być morze przytaczam złą aluzję. Ale o tym nieco dalej.

Observable.Zip

Z angielskiego Zip znaczy tyle co zamek błyskawiczny. I tym porównaniu możemy doszukać się aluzji. Działanie operatora Observable.Zip dokładnie można zrozumieć jako zamykanie zamka. Tym samym łączymy lewą i prawą część. Utworzony zostaje nowy strumień (zamknięty zamek).

Jednak na samym początku szarpnąłem się i stworzyłem dwie klasy pomocnicze PrintExtensions, oraz ConvertingExtensions. Śledząc internety natrafiłem na takie Extension Methods dzięki którym znacznie łatwiej i czytelniej jest wyświetlać dane w konsoli.

Przy pomocy DefaultPrint możemy w szybki sposób dokonać zapisu na strumień i wyświetlać skutki działania dystrybutora danych.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static IDisposable DefaultPrint<T>(this IObservable<T> source, string identify)
{
  var subscription = source.Subscribe(
    item =>
    {
      Console.WriteLine("{0}\t\titem: {1}", identify, item);
    },
    exception =>
    {
      Console.WriteLine("{0}\t\texception: {1}", identify, exception);
    },
    () =>
    {
      Console.WriteLine("{0}\t\tcompleted", identify);
    });

Druga klasa natomiast dotyczy konwertowania danych. W tym konkretnym przypadku ToCharacter wykonujemy zamianę danych typu long na ciąg znaków. Wykonujemy taką operację przez wyselekcjonowanie i zamianę wykonaną przy pomocy char.ConvertFromUtf32.

1
2
3
4
5
public static class ConvertingExtensions
{
  public static IObservable<string> ToCharacter(this IObservable<long> source)
  {
    return source.Select(item => char.ConvertFromUtf32((int)item));

Reactive Extensions - Zip Przechodząc do meritum dzisiejszego posta, przedstawiam poniżej wycinek kodu zawierający dwa obiekty publikujące cyklicznie dane. Jeden z nich będzie dostarczał dane typu long (observableIntervalForNumbers). Natomiast drugi przy pomocy wspomnianej wcześniej metodzie ToCharacter dostarczał będzie znaki.

W przypadku Observable.Zip, nie musimy się martwić o to, że dane są różnego typu. Nowy strumień będzie dostarczał je w dwóch odpowiednio nazwanych polach (Index, Character).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var observableIntervalForNumbers = Observable.Interval(TimeSpan.FromMilliseconds(10))
  .Take(256);

var observableIntervalForChars = Observable.Interval(TimeSpan.FromMilliseconds(50))
  .Take(256)
  .ToCharacter();

var subscription1 = observableIntervalForNumbers
  .Zip(observableIntervalForChars, (leftItem, rightItem) => new
  {
    Index = leftItem,
    Character = rightItem
  })
  .DefaultPrint("Zip test");

Na samym końcu korzystając z metody DefaultPrint wyrzucamy interesujące dane znajdujące się w źródełku. Używanie Zip jest bardzo proste, z głównego strumienia wywołujemy metodę i w parametrach podajemy drugi strumień z jakim będzie zip-owany. Jako drugi parametr podajemy lambdę, której celem będzie utworzenie obiektu z obu łączonych właściwości.

Zakończenie

Co jest ciekawe publikowanie na zip-owany strumień nastąpi dopiero gdy oba źródłowe strumienie wyślą swoją jednostkę danych. Zip:.color_2} poczeka, aż z obu strumieni dotrą dane.

To tyle w ten piątkowy wieczór. Po przeczytaniu posta mam cichą nadzieję, że od tej pory zamek błyskawiczny będzie miał też bardzie przyzwoite zastosowanie.

Kod z Wami!


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 - Kombinatorzy - Merge

Następny: Programowanie Reaktywne - Kombinatorzy - Switch