Cada um de nós herda dos nossos pais biológicos um conjunto de instruções químicas conhecidas como DNA que influenciam a forma como os nossos corpos são construídos. Toda a vida conhecida depende do DNA!
Nota: não precisas de perceber nada sobre nucleótidos ou DNA para completar este exercício.
O DNA é uma longa cadeia de outros químicos e os mais importantes são os quatro nucleótidos: adenina, citosina, guanina e timina. Uma única cadeia de DNA pode conter milhares de milhões destes quatro nucleótidos e a ordem em que ocorrem é importante! Chamamos "sequência de DNA" à ordem destes nucleótidos num pedaço de DNA.
Representamos uma sequência de DNA como uma coleção ordenada destes quatro nucleótidos e uma forma comum de o fazer é com uma string de carateres, como "ATTACG" para uma sequência de DNA de 6 nucleótidos. 'A' de adenina, 'C' de citosina, 'G' de guanina e 'T' de timina.
Dada uma string que representa uma sequência de DNA, conta quantos de cada nucleótido estão presentes. Se a string contiver carateres que não sejam A, C, G ou T, então é inválida e deves assinalar um erro.
Por exemplo:
"GATTACA" -> 'A': 3, 'C': 1, 'G': 1, 'T': 2
"INVALID" -> error
Um uso comum de Either é indicar uma computação que pode resultar num erro
(se o erro em si não tiver interesse, então o tipo mais simples Option pode ser uma escolha melhor).
Na ausência de um erro, o resultado costuma ser um Right (mnemónica: o valor "certo"),
enquanto um erro é um Left, por exemplo um Left[String] que contém uma mensagem de erro.
Repara que, no Scala 2.12, Either é orientado à direita por omissão, por isso funciona como esperado em operações como filter, map, flatMap ou numa compreensão for.
Se não estás familiarizado com Either, podes ler este tutorial. Mas tem em atenção que este tutorial é sobre versões de Scala anteriores à 2.12. Para Scala 2.12, podes ignorar RightProjection sem problemas e omitir .right.
Either é aquilo a que se chama uma mónada, que abrange um "aspeto computacional",
neste caso o tratamento de erros.
O uso correto de mónadas pode resultar em código muito conciso, mas ainda assim elegante
e legível. O uso incorreto pode facilmente resultar no contrário.
Vê este vídeo para saber mais.
Há algumas regras básicas para Either:
def add1(x: Int): Either[String, Int] = Right(x + 1)
é preferível:
def add1(x: Int): Int = x + 1
(há Either.map para aplicar funções simples como estas,
por isso não precisas de as sobrecarregar com Either).
isLeft/isRight ou correspondência de padrões. Por exemplo, em vez deval 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
}
é preferível:
x fold (identity, _ + 1)
Either. Em vez deval 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)))
é preferível:
for {
x <- xo
y <- yo
z <- zo
} yield x + y + z
Inscreve-te no Exercism para aprenderes e dominares Scala com 95 exercícios, e mentoria humana real, tudo grátis.