【问题标题】:Matching 3 elements at once in ocaml and converting nucleotides into acids在 ocaml 中一次匹配 3 个元素并将核苷酸转化为酸
【发布时间】:2021-07-06 11:19:16
【问题描述】:

我目前正在做一个项目,我有像[T;A;C;G;G;C;T;A;G;A;T;T;T;A;C;G;C;T;A;A;T;A;T;C] 这样的核苷酸 DNA 列表,我需要将第一条链(“START”和“STOP”)之间的核苷酸转换成相应的酸。因此,为此我需要获取 3 个 3 个核苷酸并将它们传递给这个函数:

type acid = Ala | Arg | Asn | Asp | Cys
           | Glu | Gln | Gly | His | Ile
           | Leu | Lys | Phe | Pro | Ser
           | Thr | Trp | Tyr | Val | START | STOP

let convert_acid (n1 : nucleotide) (n2 : nucleotide) (n3 : nucleotide) : acid =
  begin match (n1, n2, n3) with
  | (A, A, A) -> Phe | (A, A, G) -> Phe | (A, A, T) -> Leu  | (A, A, C) -> Leu
  | (G, A, A) -> Leu | (G, A, G) -> Leu | (G, A, T) -> Leu  | (G, A, C) -> Leu
  | (T, A, A) -> Ile | (T, A, G) -> Ile | (T, A, T) -> Ile  | (T, A, C) -> START
  | (C, A, A) -> Val | (C, A, G) -> Val | (C, A, T) -> Val  | (C, A, C) -> Val
  | (A, G, A) -> Ser | (A, G, G) -> Ser | (A, G, T) -> Ser  | (A, G, C) -> Ser
  | (G, G, A) -> Pro | (G, G, G) -> Pro | (G, G, T) -> Pro  | (G, G, C) -> Pro
  | (T, G, A) -> Thr | (T, G, G) -> Thr | (T, G, T) -> Thr  | (T, G, C) -> Thr
  | (C, G, A) -> Ala | (C, G, G) -> Ala | (C, G, T) -> Ala  | (C, G, C) -> Ala
  | (A, T, A) -> Tyr | (A, T, G) -> Tyr | (A, T, T) -> STOP | (A, T, C) -> STOP
  | (G, T, A) -> His | (G, T, G) -> His | (G, T, T) -> Gln  | (G, T, C) -> Gln
  | (T, T, A) -> Asn | (T, T, G) -> Asn | (T, T, T) -> Lys  | (T, T, C) -> Lys
  | (C, T, A) -> Asp | (C, T, G) -> Asp | (C, T, T) -> Glu  | (C, T, C) -> Glu
  | (A, C, A) -> Cys | (A, C, G) -> Cys | (A, C, T) -> STOP | (A, C, C) -> Trp
  | (G, C, A) -> Arg | (G, C, G) -> Arg | (G, C, T) -> Arg  | (G, C, C) -> Arg
  | (T, C, A) -> Ser | (T, C, G) -> Ser | (T, C, T) -> Arg  | (T, C, C) -> Arg
  | (C, C, A) -> Gly | (C, C, G) -> Gly | (C, C, T) -> Gly  | (C, C, C) -> Gly
  end

所以我的想法是获取列表的前 3 个核苷酸,通过将它们传递给函数来转换它们,并将返回的列表连接成一个新列表,但我不知道该怎么做。这是我到目前为止在伪代码中所做的:

let rec dna_to_chain (x : dna) : acid list =
  match with x
  | hd::tl -> convert_acid hd
  | _ -> do nothing

我想获取列表 x 的前 3 个元素,将它们传递给 convert_acid 函数,然后用列表的其余部分调用 dna_to_chain,直到我到达“STOP”酸。

之后,我需要执行另一个函数将每个 dna 转换为链(START 和 STOP 之间的每个核苷酸序列),并将它们放入具有类似功能的酸列表中,而不使用递归(我猜我需要使用我首先需要做的递归函数)。

有人知道我怎样才能完成我的代码并让它工作吗?谢谢!

编辑: 我现在有以下功能

let rec dna_to_chain (x : dna) : acid list =
      match with x
      | n1::n2::n3::tl -> (convert_acid n1 n2 n3) :: dna_to_chain tl
      | [] -> [] ;;

dna_to_chain [T;A;C;G;G;C;T;A;G;A;T;T ; T;A;C;G;C;T;A;A;T;A;T;C] 返回[START; Pro; Ile; STOP ; START;Arg;Leu;STOP] 但我怎样才能在第一个开始和停止之间获得第一个酸?我正在考虑创建另一个函数并再次进行模式匹配,但是我怎么知道我匹配的 START 是例如链的第一个?

【问题讨论】:

  • 这听起来像是家庭作业。您的模式 hd :: tl 仅匹配第一个核苷酸 (hd)。您可能会考虑使用nt1 :: nt2 :: nt3 :: tl 之类的模式来查看其中的三个。您可以将 3 个核苷酸翻译成酸。您还需要翻译尾部,即,您需要使用tl 进行某种递归调用。然后你可以把这两个结果放在一起。
  • 谢谢,这就是我想要的!我不知道这是可能的。现在我匹配 n1::n2::n3::tl -> (convert_acid n1 n2 n3) :: dna_to_chain tl.我不知道如何只获得 START 和 STOP 之间的第一个序列。我正在考虑创建第一个函数来执行带有 START 和 STOP 的转换,以及第二个函数仅将酸保持在 START 和 STOP 之间。我不确定这是否是正确的方法。
  • 请注意,您的dna_to_chain 函数的模式匹配并不详尽。它不考虑具有 1 个或 2 个元素的列表。此外,它的风格,但我发现首先匹配空列表案例非常有帮助(并且通常是惯用的)。

标签: list recursion functional-programming pattern-matching ocaml


【解决方案1】:

希望这是有用的答案,因为评论仅限于详细说明。

let between start_tok stop_tok lst =
  let (_, _, result) = List.fold_left 
    (fun (start_seen, acc, overall_acc) x -> 
      if start_seen && x = start_tok then 
        (true, [], overall_acc) 
      else if start_seen && x = stop_tok then
        (false, [], overall_acc @ [acc])
      else if start_seen then
        (true, acc @ [x], overall_acc)
      else if x = start_tok then
        (true, [], overall_acc)
      else
        (false, acc, overall_acc))
    (false, [], [])
    lst
  in
  result

我们折叠了一个'a list 类型的列表,提供了'a 类型的开始和停止标记。我们对折叠的初始值是一个元组,其中包含是否已看到开始标记、一个累加器和一个整体累加器。事实上,如果它在开始和停止令牌之间,它会添加到累加器中。当它停止时,该累加器被添加到整个累加器中。最后,我们使用模式匹配来访问整个累积列表。

她没有什么可以处理没有遇到启动或停止的错误。我会把它留给你做进一步的练习。

我发现折叠的限制是查看列表迭代中实际需要传达哪些信息的好方法。

【讨论】:

    【解决方案2】:

    免责声明:我不知道这是否是回答 SO 的好方法,所以如果我应该编辑我的旧答案而不是写一个新答案,请告诉我。

    所以这是我在之前的回答中谈到的拆分代码的 2 个版本。我希望它能帮助您弄清楚如何实现修改后的拆分功能。第一个使用2个递归函数,第二个是尾递归

    let rec split v = function
      | [] -> []
      | t -> let a, b = goNext v t in 
          if a <> [] then a :: split v b else split v b
    and goNext v = function
      | x :: xs -> if x = v then [], xs else 
          let a, b = goNext v xs in x :: a, b
      | [] -> ([], []);;
      
    let split2 l v = 
        let rec aux acc buff = function 
            | x :: xs -> if x = v 
                then
                  aux [] (if acc <> [] then List.rev  acc :: buff else buff) xs
                else
                  aux (x :: acc) buff xs
            | [] -> List.rev @@ if acc <> [] then List.rev acc :: buff else buff
         in aux [] [] l;;
    

    这两个函数都忽略了两次出现 v 之间的空列表

    【讨论】:

      【解决方案3】:

      首先不要将START 放在dna_to_chain 函数的结果列表中。您可以忽略它们,因为它们对您的其他问题毫无用处。

      然后你只需要写一个“拆分”函数,这是一个很好的练习。

      Split 将l ('a list)v ('a) 作为参数,并返回列表中每个v 出现之间的子列表。例如:

      split [2;3;3;1;2;3;4;5;1;2;1;3;1;8;9] 1;;
      - : int list list = [[2; 3; 3]; [2; 3; 4; 5]; [2]; [3]; [8; 9]]
      

      一旦你这样做了,你只需要在结果上使用List.map 和你的dna to chain function

      split (dna_to_chain (*your dna*)) STOP
      

      【讨论】:

      • 感谢您的帮助!但是如果 DNA 不是以 START 开头的呢?如果 DNA 无效(无 START -...- END),我需要考虑这种情况并引发异常
      • 好吧,我以为你假设 DNA 是有效的。然后,您必须编写一种拆分函数来检查第一个链是否为 START,并在 STOP 上拆分列表,并检查每个 STOP 的以下元素是否为 START(它会跳过它)或者它是 DNA 的终结
      • 所以我应该使用 fold_right 吗?我做了一个过滤 START 和 STOP 元素的函数,只使用 Liste.fold_right 保留酸,但我不知道如何构建子列表
      • 你可以用尾递归或辅助函数来做,如果你真的不知道怎么做,我可以告诉你如何制作通用的拆分函数
      • 我一直在玩并尝试实现这样的功能,但不幸的是我无法到达任何地方
      猜你喜欢
      • 2017-09-29
      • 1970-01-01
      • 2018-04-12
      • 2023-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多