Символы эликсира обрабатываются как целые числа, а не символы при разбиении на голову и хвост - PullRequest
0 голосов
/ 20 сентября 2019

Я работаю над основной проблемой с эликсиром - транскрипцией РНК.Однако я столкнулся с неожиданным (для меня) поведением своего решения:

defmodule RnaTranscription do
  @doc """
  Transcribes a character list representing DNA nucleotides to RNA

  ## Examples

  iex> RnaTranscription.to_rna('ACTG')
  'UGAC'
  """
  @spec to_rna([char]) :: [char]
  def to_rna(dna) do
    _to_rna(dna)
  end

  def _to_rna([]), do: ''
  def _to_rna([head | tail]), do: [_rna(head) | _to_rna(tail)]

  def _rna(x) when x == 'A', do: 'U' 
  def _rna(x) when x == 'C', do: 'G'
  def _rna(x) when x == 'T', do: 'A'
  def _rna(x) when x == 'G', do: 'C'
end

Когда решение запущено, я получаю ошибки, так как функция _rna вызывается с целым числом, которое не соответствует защитепредложение вместо символа.

The following arguments were given to RnaTranscription._rna/1:

        # 1
        65

    lib/rna_transcription.ex:18: RnaTranscription._rna/1
    lib/rna_transcription.ex:16: RnaTranscription._to_rna/1

Есть ли способ заставить эликсир сохранять значение как символ, когда он разделяется на голову и хвост?

Ответы [ 2 ]

1 голос
/ 20 сентября 2019

Помимо разницы между списком 'A' и символом ?A, на который отлично ответил Михаил, в этом коде есть еще один скрытый, но важный глюк.

Вы используете рекурсию, не оптимизированную для хвоста, котораяследует избегать любой ценой.В целом это может привести к переполнению стека.Ниже приведен код TCO.

defmodule RnaTranscription do
  def to_rna(dna), do: do_to_rna(dna)

  defp do_to_rna(acc \\ [], []), do: Enum.reverse(acc)
  defp do_to_rna(acc, [char | tail]),
    do: do_to_rna([do_char(char) | acc], tail)

  defp do_char(?A), do: ?U 
  defp do_char(?C), do: ?G
  defp do_char(?T), do: ?A
  defp do_char(?G), do: ?C
end

RnaTranscription.to_rna('ACTG')
#⇒ 'UGAC'

или, что еще лучше, с пониманием

converter = fn
  ?A -> ?U 
  ?C -> ?G
  ?T -> ?A
  ?G -> ?C
end

for c <- 'ACTG', do: converter.(c)         
#⇒ 'UGAC'

Вы можете даже отфильтровать его на месте.

for c when c in 'ACTG' <- 'ACXXTGXX',
  do: converter.(c)         
#⇒ 'UGAC'
1 голос
/ 20 сентября 2019

Вы можете использовать оператор кодовой точки ?:

  def _rna(x) when x == ?A, do: 'U'
  def _rna(x) when x == ?C, do: 'G'
  def _rna(x) when x == ?T, do: 'A'
  def _rna(x) when x == ?G, do: 'C'

Строго говоря, Elixir уже сохраняет значение как символ!Символ - это кодовая точка, которая является целым числом.Когда вы соответствуете по 'A', вы соответствуете по списку, который является списком целых чисел.То есть вы пытаетесь сопоставить 65 с [65].

...