【问题标题】:Use arrays and for loops but no strings or any classes [closed]使用数组和 for 循环,但不使用字符串或任何类 [关闭]
【发布时间】:2013-03-05 09:05:25
【问题描述】:
void printEncodingCodonSequences (
            char aminoAcid1,
            char aminoAcid2,
            char aminoAcid3, 
            char aminoAcid4,
            char aminoAcid5
);

给定一个由 5 个氨基酸符号组成的蛋白质片段作为输入,打印出(在屏幕上)所有的 翻译成该蛋白质片段的不同密码子序列(每行一个序列)。输入可以在 在任何情况下,如果任何输入无效,该函数应在一行中打印“?”。

这是一个例子: 蛋白质片段 MWQWH 可以由这四个密码子序列翻译,因此输入:'M','W','Q','W','H'(或'm','W','q' , 'W', 'H'),该函数应打印以下内容:

ATGTGGCAATGGCAT
ATGTGGCAGTGGCAT
ATGTGGCAATGGCAC
ATGTGGCAGTGGCAC

我不希望你回答所有问题,只是提示如何去做。对于此任务,不允许使用任何字符串或任何容器类!有人告诉我必须对数组使用嵌套循环。

在这个项目中,蛋白质片段由5个氨基酸组成(输入作为它们的符号),如果氨基酸有超过1个密码子,它将在新行中打印。

【问题讨论】:

  • 关于问题:很难给出答案的提示,因为大多数 StackOverflow 用户不知道蛋白质片段是如何工作的。你能补充一些关于这方面的信息吗?
  • 你写过代码吗?如果您假装每个氨基酸由一个独特的三元组编码,也许会更容易开始(因此输出中只有一个编码)。一旦你确定了它,你就可以开始考虑由多个三联体编码的氨基酸。

标签: c++ arrays if-statement for-loop


【解决方案1】:

鉴于您应该使用循环来实现这一点:这可以使用五个嵌套循环进行编程。在每个循环中,您将遍历 所有可能的翻译密码子三联体以获取一个氨基酸符号。这就是基本思想。

下面的伪代码可以作为开始:

for each translating codon triplet codon1 for aminoAcid1:
    for each translating codon triplet codon2 for aminoAcid2:
        for each translating codon triplet codon3 for aminoAcid3:
            for each translating codon triplet codon4 for aminoAcid4:
                for each translating codon triplet codon5 for aminoAcid5:
                    print codon1 + codon2 + codon3 + codon4 + codon5
                end for
            end for
        end for
    end for
end for

您现在要做的是使用原始数组在 C++ 中编写这些循环。对于每个氨基酸符号,您提前知道哪个(以及多少)密码子三联体是我们必须在循环中遍历的候选者。将它们存储为常量数组,并使用函数参数中提供的氨基酸在循环中访问该数组。

由于您不应该使用字符串和容器,因此您不得不使用 C 字符串(char 数组)和此类的原始数组在 C++ 中从上面对表格进行编码。伪代码中的变量 (codon1 ... codon5) 应该只是 const char*(C 字符串),并且可以使用 coutprintf 打印。

上面的表格可以写成一个数组,如下所示。请注意,我将表格的每一行都写为一个 C 字符串,它以编码氨基酸符号的字母开头,然后是一个三元组的“列表”。所有这些三元组都是以 0 结尾的(这使得将它们指向 C 字符串成为可能)。要遍历三元组,您只需找到空终止字符并将此指针加一。如果这个指针是非空的,就会有另一个三元组来。如果它是 null(请注意,在这些行的末尾,将添加另一个 null,因为它被添加到每个字符串文字中),这是最后一个三元组。

const char *codons[] = {
    "AGCT\0GCC\0GCA\0GCG\0",
    "RCGT\0CGC\0CGA\0CGG\0AGA\0AGG\0",
    // ...
    "*TAA\0TGA\0TAG\0"
};

使用从该表中找到正确行的实用函数,然后可以将循环写为(这里是第一个酸符号):

for(const char *codon1 = codonRow(aminoAcid1); codon1 += strlen(codon1); codon1 != NULL) {
    ...
}

在这个循环中,初始化使用实用函数(见下文),它从表中返回行但不包括第一个字符(因此它指向给定酸的第一个密码子三元组)。递增操作只是将这个指针增加了 C 字符串长度(这将导致 codon1 正好指向下一个空终止字符之后)。如果这是 null,我们就完成了。

好消息是,在这个循环的每次迭代中,codon1 指向一个 C 字符串(嗯,它正好指向上面数组的字符串文字的中间)并且将被适当地终止。所以打印 codon1 只会打印 3 个字符。

这是上面循环中使用的实用函数,它在表中搜索特定字符并返回这一行:

const char *codonRow(char aminoAcid)
{
    for(int i = 0; i < sizeof(codons)/sizeof(*codons); ++i) {
        const char *row = codons[i];   // Fetch the row from the array
        if (row[0] == aminoAcid)       // Compare the amino acid symbol
            return row + 1;            // Remove the amino acid symbol
    }
    // error:
    std::cerr << "No such amino acid: " << aminoAcid << std::endl;
    return 0;
}

【讨论】:

    【解决方案2】:

    嗯,您想将氨基酸序列翻译成密码子序列。因此,您需要的第一件事是将氨基酸代码转换为密码子的表格。由于不允许使用类,因此表意味着数组。由于每个氨基酸可能转化为多个密码子,因此您需要一组数组:

    #define MAX_CODONS  6   /* maximum number of codons for one amino acid */
    char *AminoAcidCodons[26][MAX_CODONS+1] = {
        /* A */ { "GCT", "GCC", "GCA", "GCT", 0 },
        /* B */ { 0 },
        /* C */ { "TGT", "TGC", 0 },
              :
        /* Y */ { "TAT", "TAC", 0 },
        /* Z */ { 0 },
    };
    

    现在您可以使用循环将大写字母转换为密码子集:

    for (i = 0; AminoAcidCodons[aminoAcid1 - 'A'][i]; i++) {
        /* AminoAcidCodons[aminoAcid1-'A'][i] is one of the codons that AminoAcid1 can be encoded as */
         :
    

    要获得 5 种氨基酸的所有可能组合,您需要 5 个嵌套循环。要处理小写字母,您需要将它们翻译成大写字母。您还需要处理无效代码(非字母和没有相应密码子的字母)。函数isalphatoupper 可以在这里提供帮助。

    【讨论】:

      【解决方案3】:

      好的,我将忽略您被告知确切有多少密码子,并描述一个更通用的解决方案。

      您知道每个密码子正好转换为 3 个字符,因此您可以预先计算所需缓冲区的大小。在您的情况下,您当然可以使用固定大小的缓冲区。请记住为终止 NUL 字符留出空间。此输出缓冲区将是一个数组。看起来像这样:

      void printEncodingCodonSequences(
              char aminoAcid1,
              char aminoAcid2,
              char aminoAcid3, 
              char aminoAcid4,
              char aminoAcid5         )
      {
           char outputBuffer[16]; // 5 codons, exactly
           char codons[] = { aminoAcid1, aminoAcid2, aminoAcid3, aminoAcid4, aminoAcid5 };
           findCodonEncodingCombinations(outputBuffer, outputBuffer, codons, 5);
      }
      

      接下来,您需要将每个密码子映射到所有可能的序列,并针对给定的密码子重复此操作。因为这是一个组合问题,所以它非常适合递归。该函数采用第一个密码子,并遍历不同的可能编码。在每次迭代中,它都会再次调用自身来处理剩余的密码子。

      int findCodonEncodingCombinations( char* outputBuffer, char* outputPos, char* codons, int remaining )
      {
          if (remaining == 0) {
              *outputPos = 0;
              puts(outputBuffer);
              puts("\n");
              return 1;
          }
      
          int combinations = 0;
      
          switch (*codon) {
              case 'D': // GAT, GAC
              case 'd':
                  outputPos[0] = 'G';
                  outputPos[1] = 'A';
                  outputPos[2] = 'T';
                  combinations += findCodonEncodingCombinations(outputBuffer, outputPos + 3, codons + 1, remaining - 1);
                  outputPos[2] = 'C';
                  combinations += findCodonEncodingCombinations(outputBuffer, outputPos + 3, codons + 1, remaining - 1);
                  break;
          }
      
          return combinations;
      }
      

      实际上,循环对于这个问题并不是特别有用。但是,您可以使用一个来验证所有输入是否有效,例如:

      void printEncodingCodonSequences(
              char aminoAcid1,
              char aminoAcid2,
              char aminoAcid3, 
              char aminoAcid4,
              char aminoAcid5         )
      {
           char outputBuffer[16]; // 5 codons, exactly
           char codons[] = { aminoAcid1, aminoAcid2, aminoAcid3, aminoAcid4, aminoAcid5 };
           if (!validateCodons(codons, 5)) return;
           findCodonEncodingCombinations(outputBuffer, outputBuffer, codons, 5);
      }
      
      bool validateCodons( char* codons, int count )
      {
          while (count--) {  // it's a loop :)
              switch (*codons++) {
                  case 'd':
                  case 'D':
                  //...
                      break;
      
                  default: // unrecognized codon
                      puts("?\n");
                      return false;
              }
          }
      
          return true;
      }
      

      显然,您需要填写许多其他密码子映射。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-23
        • 1970-01-01
        • 2015-08-02
        • 1970-01-01
        • 2014-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多