Ognuno di noi eredita dai propri genitori biologici una serie di istruzioni chimiche note come DNA, che influenzano il modo in cui il nostro corpo è costruito. Tutta la vita conosciuta dipende dal DNA!
Nota: non hai bisogno di capire nulla di nucleotidi o DNA per completare questo esercizio.
Il DNA è una lunga catena di altre sostanze chimiche, e le più importanti sono i quattro nucleotidi: adenina, citosina, guanina e timina. Una singola catena di DNA può contenere miliardi di questi quattro nucleotidi, e l'ordine in cui compaiono è importante! Chiamiamo l'ordine di questi nucleotidi in un tratto di DNA una «sequenza di DNA».
Rappresentiamo una sequenza di DNA come una raccolta ordinata di questi quattro nucleotidi, e un modo comune per farlo è una stringa di caratteri come "ATTACG" per una sequenza di DNA di 6 nucleotidi. 'A' per l'adenina, 'C' per la citosina, 'G' per la guanina e 'T' per la timina.
Data una stringa che rappresenta una sequenza di DNA, conta quanti nucleotidi di ciascun tipo sono presenti. Se la stringa contiene caratteri che non sono A, C, G o T, allora non è valida e devi segnalare un errore.
Per esempio:
"GATTACA" -> 'A': 3, 'C': 1, 'G': 1, 'T': 2
"INVALID" -> error
Un uso comune di Either è indicare un calcolo che può potenzialmente produrre un errore (se l'errore in sé non interessa, il tipo più semplice Option potrebbe essere una scelta migliore).
In assenza di un errore il risultato è di solito un Right (aiuto mnemonico: il valore «right»), mentre un errore è un Left, per esempio un Left[String] che contiene un messaggio di errore.
Nota che in Scala 2.12 Either è right-biased per impostazione predefinita, quindi funziona come previsto per operazioni come filter, map, flatMap o dentro una for-comprehension.
Se non hai familiarità con Either, puoi leggere questo tutorial. Tieni però presente che questo tutorial riguarda versioni di Scala precedenti alla 2.12. Per Scala 2.12 puoi tranquillamente ignorare RightProjection e omettere .right.
Either è una cosiddetta Monade che copre un «aspetto computazionale», in questo caso la gestione degli errori.
L'uso corretto delle monadi può portare a codice molto conciso ma elegante e leggibile. Un uso improprio può facilmente portare al contrario.
Guarda questo video per saperne di più.
Ci sono alcune regole pratiche per Either:
def add1(x: Int): Either[String, Int] = Right(x + 1)
è meglio avere
def add1(x: Int): Int = x + 1
(esiste Either.map per applicare funzioni così semplici, quindi non devi appesantirle con Either).
isLeft/isRight o il pattern matching. Per esempio, invece dival x: Either[String, Int] = ...
if (x.isRight) x.right.get + 1 else x.left.get
// or
x match {
case Right(n) => n + 1
case Left(s) => s
}
è meglio avere
x fold (identity, _ + 1)
Either. Invece dival xo: Either[String, Int] = ...
val yo: Either[String, Int] = ...
val zo: Either[String, Int] = ...
xo.flatMap(x =>
yo.flatMap(y =>
zo.map(z =>
x + y + z)))
è meglio avere
for {
x <- xo
y <- yo
z <- zo
} yield x + y + z
Iscriviti a Exercism per imparare e padroneggiare Scala con 95 esercizi e il mentoring di persone reali, tutto gratis.