Questa è una bella sfida per avere e risolverlo vi darà sicuramente alcune informazioni sulla programmazione funzionale.
La soluzione per tali problemi in lingue funzionali è in genere reduce
(spesso chiamata fold
). Inizierò con una risposta breve (e non una traduzione diretta), ma mi sento libero di chiedere un seguito.
L'approccio seguito in genere non funziona nei linguaggi di programmazione funzionali:
map = %{}
Enum.each [1, 2, 3], fn x ->
Map.put(map, x, x)
end
map
La mappa alla fine sarà ancora vuoto perchè non siamo in grado di mutare le strutture di dati. Ogni volta che chiami il numero Map.put(map, x, x)
, verrà restituita una nuova mappa. Quindi dobbiamo recuperare esplicitamente la nuova mappa dopo ogni enumerazione.
Possiamo raggiungere questo obiettivo in Elixir utilizzando ridurre:
map = Enum.reduce [1, 2, 3], %{}, fn x, acc ->
Map.put(acc, x, x)
end
Ridurre emetterà il risultato della funzione precedente come accumulatore per la voce successiva. Dopo aver eseguito il codice sopra, la variabile map
sarà %{1 => 1, 2 => 2, 3 => 3}
.
Per questi motivi, raramente si utilizza each
sull'enumerazione. Invece, utilizziamo le funzioni in the Enum
module, che supportano una vasta gamma di operazioni, eventualmente ricadendo su reduce
quando non ci sono altre opzioni.
EDIT: per rispondere alle domande e passare attraverso una traduzione più diretta del codice, questa cosa si può fare per controllare e aggiornare la mappa come si va:
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
In realtà, un insieme sarebbe meglio qui, quindi, rifattorizziamo questo e utilizzare un set nel processo:
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
Ok, fantastico, grazie che aiuta.Tuttavia, non so ancora come gestirò il controllo se un oggetto è nuovo (non nella mappa) con questo approccio? È qualcosa che accade abbastanza spesso e preferirei non usare qualcosa come un DB, anche se lo farei se dovessi farlo. Tuttavia, ha molto senso, grazie. –
Che ne dici di combinare due funzioni dal modulo Enum: membro? e filtro. In questo modo è possibile compilare l'elenco di tutti gli elementi, non un membro della lista esistente, quindi fare qualcosa. – GavinBrelstaff
Come aggiungeresti al filtro? Ogni volta che trova un post univoco, lo aggiunge all'elenco (non più unico) e svolge una funzione con esso. È possibile che una funzione si chiami da sola? –