Abbiamo una lista votes, che raccoglie tutte le preferenze sotto forma dei nomi dei candidati. Vogliamo determinare il candidato ha avuto più preferenze
Idea Funzionale:
comporre trasformazioni fino a ottenere una lista results contenente coppie nella forma (np, c), dove np è il numero di preferenze e c è il nome del candidato.
ordinare la lista per numero di preferenze.
si prende l’ultimo elemento e si estrae la seconda componente, cioè il nome del candidato vincente.
votes = ["Rossi", "Bianchi", "Verdi", "Bianchi", "Bianchi", "Rossi"]results = [(2, "Rossi"),(3, "Bianchi"),(1, "Verdi")]winner :: Ord a => [a] -> awinner = snd . last . sort . results
Ora però dobbiamo trasformare votes in results , l’idea più semplice consiste nel:
generare la lista dei candidaticdts, rimuovendo i duplicati da votes
generare lista dei numeri delle preferenze associate ad ogni candidato (usiamo filter e lenght)
uniamo le due liste per ottenere results (possiamo usare zip)
votes = ["Rossi", "Bianchi", "Verdi", "Bianchi", "Bianchi", "Rossi"]results :: Eq a => [a] -> [(Int, a)]results xs = zip (map (\x -> count x xs) cdts) cdts where count x = length . filter (==x) cdts = rmvDups xs rmvDups :: Eq a => [a] -> [a] rmvDups [] = [] rmvDups (x:xs) = x : (rmvDups (filter (/=x) xs))winner :: Ord a => [a] -> awinner = snd . last . sort . results-- Esecuzionewinner votes -- output: "Bianchi"
Considerazioni
Porta a un rapid-prototyping, pensando alle trasformazioni di una struttura dati nel suo complesso, che generano un flusso, che passa diverse funzioni per trasformare i dati nel risultato.
In questo caso rmvDups può essere ottimizzato passando da O(n^2) a O(n log n) se, effettuiamo un sorting prima di rimuovere i duplicati.
Versione di Graham Hutton (list comprehension)
results :: (Ord a) => [a] -> [(Int,a)]results vs = sort [(count v vs, v) | v <- rmvDups vs] where count x = length . filter (==x)
Ballots Voting System
Consideriamo un sistema elettorale più complesso. La lista di voti è composta non più da singole preferenza, ma da liste di preferenze in cui ciascuno mette in ordine i candidati.
Il vincitore si ottiene eliminando iterativamente quelli che ricevono meno prime scelte, ad esempio:
Per ottenere questi risultati possiamo riutilizzare la funzione results, inserendola in una funzione rank che prende una lista di ballots (lista di lista di preferenze) e utilizza la funzione results soltanto sul primo elemento di ogni lista.
Ad ogni utilizzo di rank otterremo i risultati del ballottaggio, con i partecipanti ordinati dal perdente al vincente (di quel ballottaggio).
rank :: Ord a => [[a]] -> [a] rank = map snd . results . map head
Ad ogni ballottaggio dobbiamo rimuovere il perdente (primo elemento di rank) da ogni occorrenza delle liste in ballots, per farlo possiamo usare questa funzione elim:
elim :: Eq a => a -> [[a]] -> [[a]] elim x = map (filter (/=x))
Quindi otteniamo il vincitore rimovendo ad ogni ballottaggio il perdente fino a quando non rimane un solo candidato:
winnerB :: Ord a => [[a]] -> a winnerB bs = let (c:cs) = rank (rmvEmpty bs) in if cs == [] then c -- ho finito else winnerB (elim c bs) else winnerB (elim c bs) where rmvEmpty = filter (/=[])