Introduzione
Lists are homogeneous (= same type) sequences of values, that can be processed sequentially.
The simpler way to construct a list is by using the constructor [], but this is just syntactic sugar for the infix operator cons ( :). It it can be seen as a head insert and its name derives from constructor.
Foe example this writing [1,2,3] it’s just syntactic sugar for 1:2:3:[].
x:xs
Tutte le liste hanno la forma x:xs dove:
xè la testaxsrappresenta la coda (il resto della lista)
Attenzione!
L’applicazione di funzione associa sempre più di tutto e quindi
f x:xsviene inteso come(f x):xse non comef (x:xs).
Lists Enumerations
Esistono quattro modi per dichiarare le enumerazioni di liste in Haskell:
Lista finita con passo predefinito:
[m..n]Questa notazione genera una lista finita che parte da
me arriva fino an, incrementando il valore di 1 ad ogni passo.Esempio:
[1..5]produce[1,2,3,4,5]
Lista infinita con passo predefinito:
[m..]Sfruttando la valutazione lazy di Haskell, questa notazione crea una lista infinita (uno stream) che parte da
me prosegue indefinitamente con incrementi di1.Esempio:
[1..]è la lista di tutti i numeri naturali a partire da 1.
Lista finita con passo personalizzato:
[m,n..p]Questa forma permette di definire il passo della sequenza. Il passo viene calcolato automaticamente come la differenza tra il secondo elemento (
n) e il primo (m). La lista termina al raggiungimento dip.
- Logica: Il passo è
n - m. La sequenza sarà[m, m+(n-m), m+2(n-m), ..., p].- Esempio:
[1, 3..11]produce la lista dei numeri dispari fino a `11.
Lista infinita con passo personalizzato:
[m,n..]Analogamente alla precedente, definisce il passo tramite i primi due elementi, ma genera una lista infinita.
- Esempio:
[1, 3..]genera la lista infinita di tutti i numeri dispari.
Note
- Non solo numeri: Questa notazione non è limitata agli interi, ma può essere usata con qualsiasi tipo che appartenga alla classe
Enum. Ad esempio,['a'..'z']genera correttamente la stringa contenente tutto l’alfabeto minuscolo.- Limiti logici: sebbene sia possibile definire passi costanti, la notazione non è in grado di inferire sequenze matematiche complesse. Ad esempio, scrivere
[1, 1, 2, 3, 5..]non genererà la successione di Fibonacci.
List Comprehension
Le list comprehension in Haskell sono un modo sintetico ed espressivo per manipolare le liste, ispirato direttamente alla notazione matematica della set comprehension basata sulla Logica del primo ordine (predicativa).
[x^2 | x<-[1..5]] -- [1,4,9,16,25]Fondamenti Teorici
In matematica, un insieme può essere definito come , ovvero l’insieme degli elementi
xappartenenti adAche soddisfano il predicatoP. Haskell adotta una sintassi simile:
- Generatori: La parte
x <- xsindica da dove vengono estratti gli elementi.- Filtri (Guards): I predicati inseriti dopo la virgola (es.
p x) agiscono come filtri.- Trasformazione: Espressioni come
x^2definiscono come trasformare l’elemento estratto.A livello teorico, le list comprehension non sono primitive, ma vengono tradotte internamente in termini di
mapefilter. Ad esempio:
[f x | x <- xs]equivale amap f xs.[x | x <- xs, p x]equivale afilter p xs.
Utilizzi
Le slides forniscono diversi esempi di utilizzo, dai più semplici ai più complessi:
- Trasformazioni semplici:
[x^2 | x <- [1..5]]genera i quadrati dei numeri da 1 a 5.- Pattern Matching: È possibile decomporre strutture dati direttamente nel generatore. Ad esempio, per estrarre i primi elementi di una lista di coppie:
[x | (x, _) <- ps].- Fattori di un numero: Si possono usare le “guards” per calcolare i divisori:
factors n = [x | x <- [1..n], n 'mod' x == 0].Utilizzi avanzati
- Prodotto Cartesiano: Usando più generatori, si ottengono tutte le combinazioni possibili.
[(x,y) | x <- [1,2,3], y <- [4,5]]produce tutte le coppie.- Attenzione all’ordine: L’ordine dei generatori influenza il risultato (il generatore più a destra cambia più velocemente, come un ciclo annidato).
- Flattening (concat): La funzione
concatpuò essere definita come[x | xs <- xss, x <- xs], dove si scorre prima la lista di liste e poi ogni singola lista interna.- Funtori Applicativi: La definizione standard dell’operatore applicativo
<*>per le liste usa proprio una list comprehension:gs <*> xs = [g x | g <- gs, x <- xs].
Limitazioni
Sebbene Haskell sia lazy e permetta l’uso di generatori infiniti (es.
x <- [1..]), bisogna prestare attenzione:
- Se si usa una list comprehension con un filtro su una lista infinita e si cerca un elemento che non esiste, la computazione non terminerà mai.
- Esempio critico: Cercare i fattori di un numero in una lista infinita di naturali senza un limite superiore porterà il programma a scansionare numeri all’infinito dopo aver trovato l’ultimo divisore. In questi casi è meglio usare funzioni come
takeWhile.
Funzioni su Liste
Most of the default functions in the Prelude are easy to recreate yourself, even the on that operate on lists.
Taglio: take n , drop n , splitAt n (tupla con take e drop).
take 2 [1, 2, 3, 4] -- risultato: [1, 2]
drop 2 [1, 2, 3, 4] -- risultato: [3, 4]
splitAt 2 [1, 2, 3, 4] -- risultato: ([1, 2], [3, 4])**Liste annidate/multiple:é
concat(appiattisce liste di liste),zip(unisce liste in coppie, fermandosi alla più corta)
concat [[1, 2], [3, 4]] -- risultato: [1, 2, 3, 4]
zip [1, 2] ['a', 'b', 'c'] -- risultato: [(1, 'a'), (2, 'b')]Operazioni Logiche:
and [True, False] -- risultato: False
or [True, False] -- risultato: True
any (> 2) [1, 2, 3] -- risultato: True (almeno uno è > 2)
all (> 0) [1, 2, 3] -- risultato: True (tutti sono > 0)myNull :: [a] -> Bool
myNull [] = True
myNull _ = False
myLast :: [a] -> a
myLast [x] = x
myLast (_:xs) = myLast xs
myInit :: [a] -> [a]
myInit [x] = []
myInit (x:xs) = x : myInit xs
mySum :: (Num a) => [a] -> a
mySum [] = 0
mySum (x:xs) = x + sum xs
myLenght :: [a] -> Int
myLenght [] = 0
myLenght (x:xs) = 1 + sum xs
myAnd :: [Bool] -> Bool
myAnd [] = True
myAnd (x:xs) = x && myAnd xs
myMinimum :: (Ord a) => [a] -> a
myMinimum [x] = x
myMinimum (x:xs) = min x (minimum xs)
myReverse :: [a] -> [a]
myReverse [] = []
myReverse (x:xs) = myReverse xs ++ [x]
myZip :: [a] -> [b] -> [(a,b)]
myZip [] _ = []
myZip _ [] = []
myZip (x:xs) (y:ys) = (x,y) : (zip xs ys)