Кожен із нас успадковує від своїх біологічних батьків набір хімічних вказівок, відомий як ДНК, що впливає на те, як побудоване наше тіло. Усе відоме життя залежить від ДНК!
Примітка: щоб виконати цю вправу, не потрібно розуміти нічого про нуклеотиди чи ДНК.
ДНК - це довгий ланцюг інших хімічних речовин, найважливіші з яких - чотири нуклеотиди: аденін, цитозин, гуанін і тимін. Один ланцюг ДНК може містити мільярди цих чотирьох нуклеотидів, і порядок, у якому вони розташовані, має значення! Порядок цих нуклеотидів у певній ділянці ДНК ми називаємо «послідовністю ДНК».
Ми подаємо послідовність ДНК як упорядкований набір із цих чотирьох нуклеотидів, а поширений спосіб зробити це - записати рядок тексту (англ. string), наприклад «ATTACG» для послідовності ДНК із 6 нуклеотидів. «A» означає аденін, «C» - цитозин, «G» - гуанін, «T» - тимін.
Дано рядок тексту, що задає послідовність ДНК: порахуйте, скільки є кожного з нуклеотидів. Якщо рядок тексту містить символи, які не є A, C, G або T, тоді він неправильний, і слід повідомити про помилку.
Наприклад:
"GATTACA" -> 'A': 3, 'C': 1, 'G': 1, 'T': 2
"INVALID" -> error
Поширене використання Either - позначати обчислення, яке може завершитися помилкою
(якщо сама помилка не має значення, то простіший тип Option може виявитися кращим вибором).
Якщо помилки немає, результатом зазвичай буває Right (мнемоніка: «правильне» значення),
а помилку позначає Left, наприклад Left[String] із повідомленням про помилку.
Варто зауважити, що в Scala 2.12 Either типово зміщений праворуч, тож він працює так, як очікується, для таких операцій, як filter, map, flatMap, або в конструкції for.
Якщо ми ще не знайомі з Either, можна прочитати цей посібник. Але варто памʼятати, що цей посібник стосується версій Scala до 2.12. У Scala 2.12 можна сміливо не зважати на RightProjection і не писати .right.
Either - це так звана монада, яка охоплює «обчислювальний аспект»,
а саме обробку помилок.
Правильне використання монад дає змогу писати дуже лаконічний, але елегантний
і зрозумілий код. Неправильне використання легко призводить до протилежного.
Щоб дізнатися більше, можна подивитися це відео.
Для Either є кілька орієнтовних правил:
def add1(x: Int): Either[String, Int] = Right(x + 1)
краще мати
def add1(x: Int): Int = x + 1
(є Either.map, щоб застосовувати такі прості функції,
тож не потрібно захаращувати їх Either).
2. Не варто «розгортати» значення, якщо це справді не потрібно.
Часто для цього вже є вбудовані функції. Ознаки передчасного
розгортання - це isLeft/isRight або зіставлення зі зразком. Наприклад, замість
val 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
}
краще мати
x fold (identity, _ + 1)
for. І це чудово.
Це радимо, коли потрібно «скомпонувати» кілька примірників Either. Замістьval 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)))
краще мати
for {
x <- xo
y <- yo
z <- zo
} yield x + y + z