Wzorce funkcji

Ukończone

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.