우리는 모두 생물학적 부모로부터 DNA라는 화학적 지침을 물려받아요. 이 지침은 우리 몸이 어떻게 만들어지는지에 영향을 줘요. 알려진 모든 생명체는 DNA에 의존해요!
참고: 이 연습 문제를 풀기 위해 뉴클레오타이드나 DNA에 대해 이해할 필요는 없어요.
DNA는 여러 화학 물질이 길게 이어진 사슬이고, 그중 가장 중요한 것은 아데닌, 시토신, 구아닌, 티민이라는 네 가지 뉴클레오타이드예요. DNA 사슬 하나에는 이 네 가지 뉴클레오타이드가 수십억 개나 들어갈 수 있고, 이들이 나타나는 순서가 중요해요! DNA 일부에서 이 뉴클레오타이드가 배열된 순서를 "DNA 서열"이라고 해요.
DNA 서열은 이 네 가지 뉴클레오타이드를 순서대로 모아 놓은 것으로 나타내는데, 흔히 문자를 이어 붙인 문자열로 표현해요. 예를 들어 여섯 개의 뉴클레오타이드로 이루어진 DNA 서열은 "ATTACG"처럼 써요.
'A'는 아데닌, 'C'는 시토신, 'G'는 구아닌, 'T'는 티민을 뜻해요.
DNA 서열을 나타내는 문자열이 주어지면, 각 뉴클레오타이드가 몇 개씩 있는지 세어요. 문자열에 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는 기본적으로 Right 쪽에 치우쳐 있어서, filter, map, flatMap 같은 연산이나 for 컴프리헨션에서 기대한 대로 동작해요. Either가 낯설다면 이 튜토리얼을 읽어봐도 좋아요. 다만 이 튜토리얼은 2.12 이전 버전의 Scala를 다룬다는 점을 알아두세요. 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로 코드를 어지럽힐 필요가 없어요.)
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)
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