리스트, 비트스트링, 문자열처럼 열거 가능한 값들을 재귀로 순회할 때는 흔히 두 가지가 걱정돼요.
이런 걱정거리를 해결하기 위해 _누산기_를 사용할 수 있어요.
누산기는 데이터와 함께 덧붙여 전달되는 변수예요. 함수 실행의 현재 상태를 호출이 이어질 때마다 다음 호출로 넘겨주는 데 쓰이고, 기저 사례에 도달하면 그 누산기로 재귀 호출의 최종 값을 반환해요.
누산기는 함수를 사용하는 사람이 아니라 함수를 작성하는 사람이 초기화해야 해요. 그러려면 함수를 두 개 선언해요. 필요한 데이터만 인자로 받아서 누산기를 초기화하는 공개 함수 하나와, 누산기도 함께 받는 비공개 함수 하나예요. Elixir에서는 비공개 함수 이름 앞에 do_를 붙이는 것이 흔한 패턴이에요.
# Count the length of a list without an accumulator
def count([]), do: 0
def count([_head | tail]), do: 1 + count(tail)
# Count the length of a list with an accumulator
def count(list), do: do_count(list, 0)
defp do_count([], count), do: count
defp do_count([_head | tail], count), do: do_count(tail, count + 1)
누산기를 사용하면 재귀 함수를 꼬리 재귀 함수로 바꿀 수 있어요. 함수에서 _마지막_으로 실행되는 것이 자기 자신을 호출하는 것이라면, 그 함수는 꼬리 재귀 함수예요.
DNA 연구실에서 저장 공간을 절약하려고 연구 데이터를 압축하는 여러 방법을 연구해 왔어요. 그러던 중 한 동료가 DNA 데이터를 이진 표현으로 변환하자고 제안해요.
| 핵산 | 코드 |
|---|---|
| 공백 | 0000 |
| A | 0001 |
| C | 0010 |
| G | 0100 |
| T | 1000 |
이렇게 하면 필요한 데이터 저장 비용이 절반으로 줄어들 수도 있지만, 그 대신 사람이 읽기는 어려워져요. 이 점을 곱씹어 보다가, 절약 효과를 측정해 보려고 데이터를 인코딩하고 디코딩하는 모듈을 작성하기로 해요.
encode_nucleotide/1 함수를 구현해요. 핵산의 코드 포인트를 받아 인코딩된 코드의 정수 값을 반환해요.
DNA.encode_nucleotide(?A)
# => 1
# (which is equal to 0b0001)
decode_nucleotide/1 함수를 구현해요. 인코딩된 코드의 정수 값을 받아 핵산의 코드 포인트를 반환해요.
DNA.decode_nucleotide(0b0001)
# => 65
# (which is equal to ?A)
encode/1 함수를 구현해요. 핵산과 공백을 나타내는 charlist를 받아 인코딩된 데이터의 bitstring을 반환해요.
DNA.encode(~c"AC GT")
# => <<18, 4, 8::size(4)>>
decode/1 함수를 구현해요. 핵산과 공백을 나타내는 bitstring을 받아 디코딩된 데이터를 charlist로 반환해요.
DNA.decode(<<132, 2, 1::size(4)>>)
# => ~c"TG CA"
Exercism에 가입하고 Elixir 트랙을 개념 58개연습 문제 168개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.