Julia Language
Funkcje wyższego rzędu
Szukaj…
Składnia
- foreach (f, xs)
- mapa (f, xs)
- filtr (f, xs)
- zmniejsz (f, v0, xs)
- foldl (f, v0, xs)
- foldr (f, v0, xs)
Uwagi
Funkcje mogą być akceptowane jako parametry i mogą być również tworzone jako typy zwracane. Rzeczywiście, funkcje można tworzyć wewnątrz innych funkcji. Te wewnętrzne funkcje są znane jako zamknięcia .
Funkcje jako argumenty
Funkcje są obiektami w Julii. Jak wszystkie inne obiekty, mogą być przekazywane jako argumenty do innych funkcji. Funkcje akceptujące funkcje są znane jako funkcje wyższego rzędu .
Na przykład możemy zaimplementować odpowiednik funkcji foreach
biblioteki standardowej, przyjmując funkcję f
jako pierwszy parametr.
function myforeach(f, xs)
for x in xs
f(x)
end
end
Możemy przetestować, czy ta funkcja rzeczywiście działa zgodnie z oczekiwaniami:
julia> myforeach(println, ["a", "b", "c"])
a
b
c
Przyjmując funkcję jako pierwszy parametr, zamiast późniejszego parametru, możemy użyć składni Jul blok do. Składnia do block jest po prostu wygodnym sposobem na przekazanie anonimowej funkcji jako pierwszego argumentu funkcji.
julia> myforeach([1, 2, 3]) do x
println(x^x)
end
1
4
27
Nasze wdrożenie powyższego myforeach
jest mniej więcej równoważne z wbudowaną funkcją foreach
. Istnieje również wiele innych wbudowanych funkcji wyższego rzędu.
Funkcje wyższego rzędu są dość potężne. Czasami podczas pracy z funkcjami wyższego rzędu dokładne wykonywane operacje stają się nieistotne, a programy mogą stać się dość abstrakcyjne. Kombinatory są przykładami systemów wysoce abstrakcyjnych funkcji wyższego rzędu.
Mapuj, filtruj i zmniejszaj
Dwie najbardziej podstawowe funkcje wyższego rzędu zawarte w standardowej bibliotece to map
i filter
. Funkcje te są ogólne i mogą działać na dowolnym iterowalnym . W szczególności są one odpowiednie do obliczeń na tablicach .
Załóżmy, że mamy zbiór danych szkół. Każda szkoła uczy konkretnego przedmiotu, ma kilka klas i średnią liczbę uczniów w klasie. Możemy modelować szkołę następującym niezmiennym typem :
immutable School
subject::Symbol
nclasses::Int
nstudents::Int # average no. of students per class
end
Nasz zestaw danych szkół będzie Vector{School}
:
dataset = [School(:math, 3, 30), School(:math, 5, 20), School(:science, 10, 5)]
Załóżmy, że chcemy znaleźć liczbę studentów zapisanych do programu matematycznego. Aby to zrobić, potrzebujemy kilku kroków:
- musimy zawęzić zestaw danych do tylko szkół uczących matematyki (
filter
) - musimy obliczyć liczbę uczniów w każdej szkole (
map
) - i musimy zredukować listę studentów do jednej wartości, sumy (
reduce
)
Naiwnym (mało wydajnym) rozwiązaniem byłoby po prostu bezpośrednie użycie tych trzech funkcji wyższego rzędu.
function nmath(data)
maths = filter(x -> x.subject === :math, data)
students = map(x -> x.nclasses * x.nstudents, maths)
reduce(+, 0, students)
end
i potwierdzamy, że w naszym zestawie danych znajduje się 190 studentów matematyki:
julia> nmath(dataset)
190
Istnieją funkcje, które łączą te funkcje i tym samym poprawiają wydajność. Na przykład moglibyśmy użyć funkcji mapreduce
do wykonania mapowania i redukcji w jednym kroku, co oszczędziłoby czas i pamięć.
reduce
ma znaczenie tylko dla skojarzonych operacji takich jak +
, ale czasami jest to przydatne do przeprowadzenia redukcji za pomocą nie-asocjatywnym pracy. Funkcje wyższego rzędu foldl
i foldr
są dostarczane w celu wymuszenia określonego rzędu redukcji.