defmodule ProteinTranslation do @doc """ Given an RNA string, return a list of proteins specified by codons, in order. """ @spec of_rna(String.t()) :: {:ok, list(String.t())} | {:error, String.t()} def of_rna(""), do: {:ok, []} def of_rna(rna) do rna |> String.graphemes() |> Enum.chunk_every(3) |> Enum.map(&Enum.join/1) |> Enum.reduce_while([], &reducer/2) |> case do proteines when is_list(proteines) -> {:ok, Enum.reverse(proteines)} {:error, error} -> {:error, error} end end defp reducer(codon, proteines) do case of_codon(codon) do {:error, _error} -> {:halt, {:error, "invalid RNA"}} {:ok, "STOP"} -> {:halt, proteines} {:ok, proteine} -> {:cont, [proteine | proteines]} end end @doc """ Given a codon, return the corresponding protein UGU -> Cysteine UGC -> Cysteine UUA -> Leucine UUG -> Leucine AUG -> Methionine UUU -> Phenylalanine UUC -> Phenylalanine UCU -> Serine UCC -> Serine UCA -> Serine UCG -> Serine UGG -> Tryptophan UAU -> Tyrosine UAC -> Tyrosine UAA -> STOP UAG -> STOP UGA -> STOP """ @cysteines ~w(UGU UGC) @leucines ~w(UUA UUG) @tyrosines ~w(UAU UAC) @phenylalanines ~w(UUU UUC) @serines ~w(UCU UCC UCA UCG) @methionines ~w(AUG) @tryptophans ~w(UGG) @stops ~w(UAA UAG UGA) @spec of_codon(String.t()) :: {:ok, String.t()} | {:error, String.t()} def of_codon(cysteine) when cysteine in @cysteines, do: {:ok, "Cysteine"} def of_codon(leucine) when leucine in @leucines, do: {:ok, "Leucine"} def of_codon(tyrosine) when tyrosine in @tyrosines, do: {:ok, "Tyrosine"} def of_codon(phenylalanine) when phenylalanine in @phenylalanines, do: {:ok, "Phenylalanine"} def of_codon(serine) when serine in @serines, do: {:ok, "Serine"} def of_codon(methionine) when methionine in @methionines, do: {:ok, "Methionine"} def of_codon(tryptophan) when tryptophan in @tryptophans, do: {:ok, "Tryptophan"} def of_codon(stop) when stop in @stops, do: {:ok, "STOP"} def of_codon(_invalid), do: {:error, "invalid codon"} end