Jest to miłe wyzwanie, które trzeba rozwiązać, a na pewno da wgląd w funkcjonalne programowanie.
Rozwiązaniem dla takich problemów w językach funkcjonalnych jest zwykle reduce
(często nazywane fold
). Zacznę od krótkiej odpowiedzi (a nie tłumaczenia bezpośredniego), ale nie krępuj się poprosić o kontynuację.
Poniższa podejście zazwyczaj nie działa w językach programowania funkcjonalnych:
map = %{}
Enum.each [1, 2, 3], fn x ->
Map.put(map, x, x)
end
map
Mapa na końcu wciąż będzie pusty, ponieważ nie możemy mutować struktury danych. Za każdym razem, gdy wywołasz Map.put(map, x, x)
, zwróci ona nową mapę. Musimy więc jawnie pobrać nową mapę po każdym wyliczeniu.
Możemy to osiągnąć w Elixir użyciu zmniejszają:
map = Enum.reduce [1, 2, 3], %{}, fn x, acc ->
Map.put(acc, x, x)
end
Zmniejszyć wyemituje wynik poprzedniej funkcji jako akumulatora do następnego elementu. Po uruchomieniu powyższego kodu zmienną będzie %{1 => 1, 2 => 2, 3 => 3}
.
Z tych powodów rzadko używamy each
podczas wyliczenia. Zamiast tego korzystamy z funkcji w the Enum
module, które obsługują szeroki zakres operacji, ostatecznie wracając do reduce
, gdy nie ma innej opcji.
EDIT: aby odpowiedzieć na pytania i przejść na bardziej bezpośrednim tłumaczeniu kodu, to co można zrobić, aby sprawdzić i zaktualizować mapę as you go:
Enum.reduce blogs, %{}, fn blog, history ->
posts = get_posts(blog)
Enum.reduce posts, history, fn post, history ->
if Map.has_key?(history, post.url) do
# Return the history unchanged
history
else
do_thing(post)
Map.put(history, post.url, true)
end
end
end
W rzeczywistości, zestaw będzie lepiej tutaj, więc niech to byłaby to i użyć zestawu w procesie:
def traverse_blogs(blogs) do
Enum.reduce blogs, HashSet.new, &traverse_blog/2
end
def traverse_blog(blog, history) do
Enum.reduce get_posts(blog), history, &traverse_post/2
end
def traverse_post(post, history) do
if post.url in history do
# Return the history unchanged
history
else
do_thing(post)
HashSet.put(history, post.url)
end
end
Okay, niesamowite, dzięki, że pomaga.Jednak nadal nie wiem, jak poradziłbym sobie z sprawdzaniem, czy przedmiot jest nowy (nie na mapie) z tym podejściem? To coś, co dzieje się dość często i wolałbym nie używać czegoś takiego jak DB, chociaż chciałbym, gdybym musiał. Ma jednak wiele sensu, dziękuję. –
Co powiesz na połączenie dwóch funkcji z modułu Enum: member? i filtrować. W ten sposób możesz skompilować listę wszystkich elementów, które nie są członkami istniejącej listy, a następnie zrobić coś. – GavinBrelstaff
Jak dodać do filtra? Ilekroć znajdzie unikalny post, dodaje go do listy (nie jest już unikalny) i wykonuje z nią funkcję. Czy funkcja może wywoływać samą siebie? –