map
Si supponga di voler scrivere una funzione scaleList
che, dati una lista di interi xs
e un numero intero factor
, restituisca una nuova lista ottenuta moltiplicando ogni elemento di xs
per factor
.
def scaleList(xs: List[Int], factor: Int): List[Int] = xs match {
case Nil => Nil
case y :: ys => (y * factor) :: scaleList(ys, factor)
}
Questo schema è generalizzato, dalla funzione map
, che applica un arbitraria funzione a tutti gli elementi di una lista. Essa funzione è disopnibile nella libreria standard di Scala come metodo della classe List[+T]
(e di molte altre strutture dati). Concettualmente, la sua implementazione potrebbe essere la seguente:
sealed abstract class List[+T] {
// ...
def map[U](f: T => U): List[U] = this match {
case Nil => Nil
case x :: xs => f(x) :: xs.map(f)
}
// ...
}
(la reale implementazione evita gli svantaggi della ricorsione non in coda presente in quest’implementazione semplificata facendo uso delle parti imperative di Scala). Si osservi che il tipo di map
è parametrico nel codominio U
della funzione f
applicata agli elementi della lista originale, ovvero nel tipo degli elementi della nuova lista, che sono appunto generati applicando la funzione f
e quindi appartengono al codominio di tale funzione.
Utilizzando map
la funzione scaleList
può essere riscritta come:
def scaleList(xs: List[Int], factor: Int): List[Int] =
xs map (x => x * factor)
Mentre i metodi di List[+T]
visti in precedenza sono funzioni al primo ordine, il metodo map
appena introdotto è un esempio di funzione all’ordine superiore, in quanto prende come argomento una funzione.
In generale, le funzioni all’ordine superiore sulle liste (map
, e alre che verranno presentate nel seguito) appartengono a tre principali famiglie:
Questi tipi di funzioni esistono non solo per le liste, ma per praticamente tutte le strutture dati.
filter
Un esempio di filtering è la seguente funzione, che restituisce la lista di tutti i valori positivi contenuti in una lista di interi:
def posElem(xs: List[Int]): List[Int] = xs match {
case Nil => Nil
case y :: ys => if (y > 0) y :: posElem(ys) else posElem(ys)
}
Generalizzando lo schema di posElem
si ottiene il metodo filter
, che è definito nella classe List[+T]
e potrebbe essere implementato in questo modo:
sealed abstract class List[+T] {
// ...
def filter(p: T => Boolean): List[T] = this match {
case Nil => Nil
case x :: xs => if (p(x)) x :: xs.filter(p) else xs.filter(p)
}
// ...
}