Im więcej czytam i bawię się Elixir tym więciej jest rzeczy, szczegółów które powodują, że stoję w miejscu i nie idę dalej. Tym razem zatrzymałem się na pattern matching. To nie jest nic trudnego. Kwestia taka, że się okazało iż jest to podstawą Elixira i wszędzie tam jest pattern matching. WSZĘDZIE :) Dlatego mały stop, i kilka informacji o tym co to jest dopasowanie wzorcowe i po co ono jest. W kolejnym odcinku Elixir zaś pokarzę jak to jest zrobione w elixirze.

Co to jest pattern matching?

Jak podaje wikipedia.

pattern matching is the act of checking a given sequence of tokens for the presence of the constituents of some pattern

Czyli ogólnie chodzi o to, że jedna strona jest porównywana do drugiej i jak są dopasowania to wszystko wartości są bindowane albo jest wykonywana funkcja. Jeżeli nie ma to albo występuje wyjątek albo jest to pomijane. W C# istnieje częściowe wsparcia dla pattern matching i w C# 7 ma być to bardziej rozwinięte.

Przykłady w innych językach niż Elixir

Jeżeli korzystaliście z FluentValidation to na przykład następujący kod:

When(x => x.NullableBool.IsTrue(), () => { });
When(x => x.NullableBool.IsFalse(), () => { });
When(x => x.NullableBool.IsNull(), () => { });

Można podciągnąć trochę pod pattern matching (NIE NIE MOŻNA ale nie znalazłem lepszego przykładu, może ktoś?). W sensie takim, że mamy jakiś warunek, i następnie w zależności od jego wartości wykonujemy odpowiednią operację. Jest to dość ekspresywne, więc może przykład z F# pomoże:

let rec fib n =
    match n with
    | 0 -> 0
    | 1 -> 1
    | _ -> fib (n - 1) + fib (n - 2)

Tutaj mamy funkcję rekurencyjną – ciąg Fibonacciego – która przyjmuje jakąś wartość. Ta wartość jest porównywana ze wzorcem po lewej stronie 0, 1 i _. Gdzie 0 jest tylko wtedy dopasowane jak wartość n jest 0. Tak samo jest z 1. Zaś _ dopasowuje wszystko, każdą wartość. Przy każdym dopasowaniu zwracana jest wartość operacji ->.

Innym przykładem dopasowania wzorca może być:

let f 0 = 1

W którym dosłownie mówimy, że mamy funkcję, która czeka na wartość 0. Każde jej wywołanie:

f 10

Zrobi match do wartości 0. Jeżeli pasuje, to funkcja zostanie wykonana (zwróci 1), jeżeli nie to dostaniemy:

Microsoft.FSharp.Core.MatchFailureException: The match cases were incomplete
   at FSI_0007.f(Int32 _arg1)
   at <StartupCode$FSI_0009>.$FSI_0009.main@() in d:\Program.fs:line 25
Stopped due to error

Jak widzicie pattern matching to coś więcej niż if, switch itp. Zwykły if nie jest dopasowaniem i nie będzie. Do pattern matchingu potrzebne są odpowiednie algorytmy. Podobno w C# 7 pattern matching będzie częścią języka. Zobaczymy.

Co umożliwia nam pattern matching?

A no umożliwia nam on napisanie skomplikowanej logiki w prosty i przyjazny sposób – umożliwia nam się skoncentrowanie na tym na co mamy zamienić input niż jak mamy to opakować i ile zbędnych linijek kodu napisać. Dla przykładu, przekazujemy początek i koniec linii jako start (x,y) i end (x,y) – zauważcie porównanie x1 = x2, a dokładnie nie porównanie a pattern matching w akcji który zadziała tylko wtedy kiedy dwie wartości będą takie same:

let matchOnTuples start ends = 
    match (start, ends) with 
    | (1, _), (1, _) -> "it starts and ends in same place (1)"
    | (_, 2), (_, 2) -> "it starts and ends on same height (2)"
    | (x1, _), (x2, _) when x1 = x2 -> "it starts in same place"
    | _ -> "We don't care"

Napisanie tego w obiektowym języku bez wsparcia dopasowania nie było by takie eleganckie:

public string matchOnTuples (Point start, Point end)
{
	if(start.X == 1 && end.X == 1) 
	{ 
		return  "it starts and ends in same place (1)" ; 
	}
	else if(start.Y == 1 && end.Y == 1) 
	{ 
		return  "it starts and ends on same height (2)" ; 
	}
	else if(start.X == end.X)
	{
		return ""it starts in same place";
	}
	else
	{
		return "We don't care";
	}
}

To jest prosty przykład. Łatwo sobie wyobrazić bardziej zaawansowane skomplikowane obliczenia, gdzie nie chcemy jakiś prosty ifów, ale skomplikowane wzorce dopasowania dla własnych typów z możliwością przeciążenia każdego przez dodatkowy warunek when pozwalający na ustawienia strażnika.

Podsumowanie

Pokazałem proste zastosowania dopasowania wzorca jak i także użyłem dość uproszczonego języka. Nie pokazałem wszystkich możliwości i typów pattern matchingu. Ale chyba to co pokazałem powinno pomóc zrozumieć na czym dopasowanie polega. Jest to potężne narzędzie, które jest dane programistom. Jak popatrzycie na kod F# to pattern matching będzie prawie wszędzie. Dosłownie będzie on wszędzie bo nawet przy wywołaniu funkcji będzie on wykorzystany.

Nie jest to też trudne do zrozumienia. Kwestia przestawienia się z myślenia wykonania wyrażenia zwracającego wartość bool, do dopasowania wzorca.

Jeżeli zaś uważasz, że powinienem więcej na ten temat opisać albo, że spłaszczyłem temat to daj znać, rozwinę go jak będzie trzeba. Jeżeli zaś czegoś nie rozumiesz – dodaj komentarz, z chęcią wytłumaczę.

PS.: Wybrałem C# i F# jako, że z nimi miałem najwięcej styczności.

2 KOMENTARZE

Comments are closed.