如何加入Elixir

时间:2017-02-27 18:59:43

标签: elixir

我正在编写一个将 RNA 转换为 DNA 的函数。输入以char列表形式出现:'ATCG'所以我遍历每个字母并使用地图来获取转换后的字母。简单。

问题是在枚举字符列表后我无法将其合并回char列表。 Enum.join返回一个字符串,to_charlist只返回一个字符点列表,我没有看到任何其他可以提供帮助的函数。

这是我的代码:

def to_rna(dna) do
  dna_to_rna = %{
   'G' => 'C',
   'C' => 'G',
   'T' => 'A',
   'A' => 'U'
  }
  Enum.map(dna, fn(letter) ->
    Map.get(dna_to_rna, [letter])
  end)    
end

这会输出一个字符列表:['U', 'G', 'C']。如何将此列表转换为charlist:'UGC'

4 个答案:

答案 0 :(得分:5)

连接列表

您最后可以使用Enum.concat/1加入列表:

Enum.concat(['U', 'A', 'G', 'C'])      # =>   'UAGC'

使用Reducer

您也可以使用reduce/3代替map/2并执行一步:

def to_rna(dna) do
  mapping = %{
   'G' => 'C',
   'C' => 'G',
   'T' => 'A',
   'A' => 'U'
  }

  Enum.reduce(dna, [], fn(letter, acc) ->
    acc ++ Map.get(mapping, [letter])
  end)  
end

这将为输入值'UAGC'提供'ATCG'

答案 1 :(得分:4)

不是 necropost,但我最近开始学习 Elixir 并且一直在做一些锻炼问题。 这个答案可能有点偏离主题,因为我提出了一个类似的解决方案, 不使用 Map

如上所述,Exercism 问题的 (rna-transcription, no link) 函数输入是一个 charlist 并期望图表师作为输出。 来自字符列表文档:

<块引用>

字符列表是一个整数列表,其中所有整数都是有效的代码点。

我们还可以查看 Exercism 提供的 function spec@spec to_rna([char]) :: [char] 和看到一个字符列表是预期的输入和输出。 built-in types documentation 向我们显示 charlist()[char()] 相同。

从问题的描述中,我们看到代码点到代码点的 1:1 映射。地图是一种完全有效的表示方式,但我认为我们可以仅使用迭代和模式匹配来解决问题。

我觉得其他一些答案中遗漏了一个重点:为什么 Enum 元素用 [] 包裹,例如Enum.map(dna, fn x -> Map.get(%{...}, [x])? 这是因为每个迭代元素都是单个字符,即整数/代码点。通过将值包装在 [] 中,该代码点被转换为具有一个字符的图表,例如'C'。示例 Map 的键/值都是单元素字符列表。这成功地映射了核苷酸到核苷酸,但它也将元素的类型从整数更改为字符列表。 这会产生一个字符列表,而不是单个/平面字符列表,例如'GCTA' 变成 ['C', 'G', 'A', 'U'](字符列表),'CGAU' 相同。 正如其他解决方案中所指出的,Enum.concat/1Enum.flat_map/2 都是将列表列表变成平面图表的方法。来自 flat_map docs

<块引用>

... 从概念上讲,[flat_map/2] 类似于 map/2concat/1 的组合。

那么,我们可以在没有中间字符列表的情况下执行映射吗?我相信答案是肯定的。 与其将每个代码点转换为字符列表,不如直接将代码点转换为预期的代码点?

从上面的文档链接,我们知道代码点只是整数。演示:

iex[1]> Enum.each('CGAU', &IO.inspect/1)
67
71
65
85
:ok

这意味着我们真的只需要将整数/代码点值映射到其他代码点值。 但是,当我们尝试映射字符文字时,我觉得映射整数值有点不自然。 幸运的是,Elixir 为我们提供了 ? 符号,它表示字符字面量 (more here) 的整数值,例如

iex> ?G
71
iex[1]> 'ATCG' == [?A, ?T, ?C, ?G]
true
iex[2]> [?A, ?T, ?C, ?G] == [65, 84, 67, 71]
true

使用这个,我们可以直接将单个代码点转换为它们各自的代码点映射,同时仍然使用字符文字,例如 ?G -> ?C。您仍然可以制作其中的 Map,例如%{?G => ?C, ...};但是,我觉得这是模式匹配的好场景。 我们可以将 Enum.map 与具有多个子句的函数一起使用。 使用这种方法,单个代码点被 1:1 映射,从而产生一个新的平面字符列表——允许我们放弃 concatflat_map(并节省一些输入)。

@spec to_rna([char]) :: [char]
def to_rna(dna) do
    Enum.map(dna, fn
        ?G -> ?C
        ?C -> ?G
        ?T -> ?A
        ?A -> ?U
    end)
end

答案 2 :(得分:0)

您可以使用Enum.mapEnum.concat来解决它:

  def to_rna(dna) do
    dna
    |> Enum.map(fn x -> Map.get(%{'G' => 'C', 'C' => 'G', 'T' => 'A', 'A' => 'U'}, [x]) end)
    |> Enum.concat()
  end

答案 3 :(得分:-1)

  :lists.flatten(['A', 'B','C'])

这是列表模块中的erlang函数。在elixir中,您可以将erlang函数调用为:。