Chapter 6: Patterns all the way down
DNA encoding
- bit arrays
- binary literals
Each nucleotide fits in two bits. <<acc:bits,
encode_nucleotide(nucleotide):2>> appends two bits to a bit
array.
Decoding runs the other way: every clause of do_decode
matches two bits off the front with <<0b01:2,
rest:bits>>. Bit-level parsing ends up looking like a
table.
import gleam/io
import gleam/list
import gleam/string
pub type Nucleotide {
Adenine
Cytosine
Guanine
Thymine
}
pub def encode_nucleotide(nucleotide: Nucleotide) -> Int {
case nucleotide {
Adenine -> 0b00
Cytosine -> 0b01
Guanine -> 0b10
Thymine -> 0b11
}
}
pub def decode_nucleotide(nucleotide: Int) -> Result(Nucleotide, Nil) {
case nucleotide {
0b00 -> Ok(Adenine)
0b01 -> Ok(Cytosine)
0b10 -> Ok(Guanine)
0b11 -> Ok(Thymine)
_ -> Error(Nil)
}
}
pub def encode(dna: List(Nucleotide)) -> BitArray {
do_encode(dna, <<>>)
}
def do_encode(dna: List(Nucleotide), acc: BitArray) -> BitArray
def do_encode([], acc) {
acc
}
def do_encode([nucleotide | rest], acc) {
do_encode(rest, <<acc:bits, encode_nucleotide(nucleotide):2>>)
}
pub def decode(dna: BitArray) -> Result(List(Nucleotide), Nil) {
do_decode(dna, [])
}
def do_decode(<<>>, acc) {
Ok(list.reverse(acc))
}
def do_decode(<<0b00:2, rest:bits>>, acc) {
do_decode(rest, [Adenine | acc])
}
def do_decode(<<0b01:2, rest:bits>>, acc) {
do_decode(rest, [Cytosine | acc])
}
def do_decode(<<0b10:2, rest:bits>>, acc) {
do_decode(rest, [Guanine | acc])
}
def do_decode(<<0b11:2, rest:bits>>, acc) {
do_decode(rest, [Thymine | acc])
}
def do_decode(_, _) {
Error(Nil)
}
pub def main() {
dna = [Adenine, Cytosine, Guanine, Thymine, Thymine]
bits = encode(dna)
io.println(string.inspect(bits))
io.println(string.inspect(decode(bits) == Ok(dna)))
io.println(string.inspect(decode(<<0b1:1>>)))
header = <<"DNA":utf8, 3:size(8), 255:8-big>>
<<"DNA":utf8, len:8, rest:bits>> = header
io.println(string.inspect(#(len, rest)))
}import gleam/io
import gleam/list
import gleam/string
pub type Nucleotide {
Adenine
Cytosine
Guanine
Thymine
}
pub fn encode_nucleotide(nucleotide: Nucleotide) -> Int {
case nucleotide {
Adenine -> 0b00
Cytosine -> 0b01
Guanine -> 0b10
Thymine -> 0b11
}
}
pub fn decode_nucleotide(nucleotide: Int) -> Result(Nucleotide, Nil) {
case nucleotide {
0b00 -> Ok(Adenine)
0b01 -> Ok(Cytosine)
0b10 -> Ok(Guanine)
0b11 -> Ok(Thymine)
_ -> Error(Nil)
}
}
pub fn encode(dna: List(Nucleotide)) -> BitArray {
do_encode(dna, <<>>)
}
fn do_encode(arg_0: List(Nucleotide), acc: BitArray) -> BitArray {
case arg_0 {
[] -> acc
[nucleotide, ..rest] ->
do_encode(rest, <<acc:bits, encode_nucleotide(nucleotide):2>>)
}
}
pub fn decode(dna: BitArray) -> Result(List(Nucleotide), Nil) {
do_decode(dna, [])
}
fn do_decode(arg_0, arg_1) {
case arg_0, arg_1 {
<<>>, acc -> Ok(list.reverse(acc))
<<0b00:2, rest:bits>>, acc -> do_decode(rest, [Adenine, ..acc])
<<0b01:2, rest:bits>>, acc -> do_decode(rest, [Cytosine, ..acc])
<<0b10:2, rest:bits>>, acc -> do_decode(rest, [Guanine, ..acc])
<<0b11:2, rest:bits>>, acc -> do_decode(rest, [Thymine, ..acc])
_, _ -> Error(Nil)
}
}
pub fn main() {
let dna = [Adenine, Cytosine, Guanine, Thymine, Thymine]
let bits = encode(dna)
io.println(string.inspect(bits))
io.println(string.inspect(decode(bits) == Ok(dna)))
io.println(string.inspect(decode(<<0b1:1>>)))
let header = <<"DNA":utf8, 3:size(8), 255:8-big>>
let assert <<"DNA":utf8, len:8, rest:bits>> = header
io.println(string.inspect(#(len, rest)))
}<<27, 3:size(2)>>
True
Error(Nil)
#(3, <<255>>)An exercism exercise from the Gleam track, solved in Bea: beacode/src/e23_dna_encoding.bea