Wzorce funkcji
Do tej pory wykonano repetytucyjne wyrażenia i umieściliśmy te wyrażenia w funkcjach. Twój kod wygląda teraz lepiej i jest łatwiejszy do odczytania. W miarę używania funkcji warto przyjrzeć się niektórym zaawansowanym wzorcom, które istnieją w tej przestrzeni. Korzystając z tych wzorców, uzyskasz kod, który jest łatwiejszy do odczytania i konserwacji.
Deklaratywne a imperatywne
Po rozpoczęciu kodowania najprawdopodobniej napiszesz wyrażenie. Następnie piszesz kolejne wyrażenie, po którym następuje drugie, po drugim. Skupiasz się na rozwiązywaniu problemu i jesteś konkretny w jaki sposób go rozwiązujesz. Takie podejście jest określane jako podejście imperatywne . Nie ma z tym nic złego, ponieważ rozwiązuje problem pod ręką. Ale jest inna droga do podjęcia, która jest podejściem deklaratywnym . Przykład podejścia deklaratywnego można zobaczyć podczas wykonywania zapytań względem bazy danych przy użyciu języka SQL.
Oto przykładowe wyrażenie:
SELECT *
FROM Students s
WHERE s.Location = "Ohio"
To, co sprawia, że ten kod jest deklaratywny, jest to, że pytasz o to, co chcesz, ale nie określasz sposobu rozwiązania problemu. Pozostaw instrukcje dotyczące języka SQL.
Możesz również zastosować to podejście do języka F#. Poniższy kod używa podejścia deklaratywnego:
let studentsFromOhio =
allStudents
|> filterLocation "Ohio"
W poprzednim kodzie wykonujesz operacje na danych i pytasz o to, czego potrzebujesz, bez konieczności wprowadzania szczegółowych informacji o tym, jak chcesz to zrobić. Gdy kod wygląda jak w poprzednim przykładzie, łatwo jest czytać i rozumować. Aby przejść do tego punktu, przyjrzyjmy się niektórym przydatnym wzorom obsługiwanym w języku F#.
Wzorce funkcjonalne
Istnieje kilka przydatnych wzorców w języku F#, których można użyć do stosowania bardziej funkcjonalnego podejścia. Omówimy następujące wzorce:
- Kompozycja: Kompozycja łączy wiele funkcji ze sobą w jedną funkcję.
- Potok: potok rozpoczyna się od wartości, a następnie sekwencyjnie wywołuje wiele funkcji przy użyciu danych wyjściowych z jednej funkcji jako danych wejściowych dla następnej funkcji.
Tworzenie
Kompozycja polega na połączeniu funkcji i zastosowaniu ich, jeden po drugim, w określonej kolejności. Operator kompozycji przyjmuje dwie funkcje i zwraca nową funkcję.
Podczas tworzenia kodu często będziesz wywoływać jedną funkcję, a następnie drugą funkcję bezpośrednio po niej. Możesz na przykład zamówić listę i odfiltrować wszystkie produkty, które są rabatem.
W poniższym przykładzie funkcja add2() jest wywoływana, a jej wynik jest podawany multiply3() do funkcji.
let add2 a = a + 2
let multiply3 a = a * 3
let addAndMultiply a =
let sum = add2 a
let product = multiply3 sum
product
printfn "%i" (addAndMultiply 2) // 12
Ten wzorzec jest tak typowy, że język F# ma dla niego operator. Operator >> umożliwia łączenie co najmniej dwóch funkcji w jedną większą funkcję. Za pomocą >> operatora możesz uprościć poprzedni kod, w następujący sposób:
let add2 a = a + 2
let multiply3 a = a * 3
let addAndMultiply = add2 >> multiply3
printfn "%i" (addAndMultiply 2) // 12
Funkcja połączona addAndMultiply() stosuje funkcje, które składa się od lewej do prawej. W tym przykładzie add2() następuje najpierw i multiply3() dzieje się ostatnio.
rurociąg
Operator |> potoku przyjmuje funkcję i argument i zwraca wartość. Zobaczmy, jak potok różni się od kompozycji w tym przykładzie:
let list = [4; 3; 1]
let sort (list: int list) = List.sort list
let print (list: int list)= List.iter(fun x-> printfn "item %i" x) list
list |> sort |> print // item 1 item 3 item 4
W ostatnim wierszu kodu zaczynasz od listy liczb całkowitych , listktóra służy jako dane wejściowe do pierwszej funkcji sort(). Wynik tej operacji jest wprowadzany do print()elementu . Główną różnicą między potokiem a kompozycją jest to, że w przypadku potoku zaczynasz od niektórych danych, czyli listy liczb całkowitych w tym przypadku, a następnie prowadzisz ją za pomocą zestawu funkcji.