【问题标题】:K&R Exercise 1-21 - Mental incomprehensionK&R 练习 1-21 - 心理不理解
【发布时间】:2010-06-11 03:34:35
【问题描述】:

“不可能”的 K&R 练习。

"写一个程序entab来替换 最少的空格字符串 要实现的制表符和空格数 相同的间距。使用相同的选项卡 停止,说每 n 列。应该 是一个变量或一个符号 参数?”

我遇到的问题是,我不确定如何正确执行此操作。我知道这不是很容易解释,但这几乎就是这里的问题。我见过的大多数示例都计算了一些空白,并用选项卡替换了这些系列,但这不是它的要求,我想我理解它的要求,但目前感觉无法做到这一点。

谁能帮忙:)

编辑:到目前为止我写的代码can be found here

【问题讨论】:

  • 您的问题到底是什么?到目前为止你做了什么?
  • 史蒂文:你好!设法写了一些糟糕的代码:) codepad.org/9xESoMQ0 我的问题是我的代码应该做什么,不是我应该如何解决这个问题?
  • 您对任务的理解是什么,这与您看到的示例有何不同?
  • 吉姆:感谢您的回复!与其他字符不同,制表位独立于字符串中的位置,因此制表符的起始位置和结束位置始终是恒定的,并且不依赖于文本的当前位置实际位置。这与之前的练习不同,因为它需要更大的精神飞跃:)
  • 可以假设输入由所有空格组成,这大大简化了问题!但考虑到上下文(前面的detab 练习)你可能是对的,这不是他们想要的。

标签: c kr-c


【解决方案1】:

如果您的问题是“这是要我做什么?”我想我可以通过解释原始问题来提供帮助(以不同的方式提出相同的问题)。

编写一个程序,将带空格的文本作为输入文本,并尽可能使用制表符生成视觉等效文本作为输出。

例如,每 8 个字符有一个制表位,并将空格显示为 '.'和标签为'-';

input;
".foo:...bar;......#comment"
output;
".foo:-bar;-..#comment"

input;
".......-foo:.....bar;......#comment"
output;
"-foo:-.bar;-...#comment"

编写程序,使制表符参数 n 可以变化,即允许 n 的值不为 8。准备好证明您决定将 n 设为常数或变量的合理性。

编辑我看了你的代码,我认为它比它需要的更复杂。我的建议是一次做一个角色。无需缓冲整行。在读取每个字符时保持列计数('\n' 将其重置为零,'\t' 将其增加 1 或更多,其他字符将其增加)。当你看到一个空格(或制表符)时,不要立即发出任何内容,开始你的 entabbing 过程,发出零个或多个制表符,然后是空格(在 '\n' 或非空白字符处,以先到者为准)。

最后一个提示是,状态机可以使这种算法更易于编写、验证、测试和阅读。

编辑 2 为了让 OP 接受我的回答,我现在已经继续并根据我上面提供的提示和我在讨论中的评论自己编写了一个解决方案.

// K&R Exercise 1-21, entab program, for Stackoverflow.com
#include <stdio.h>
#define N 4     // Tabstop value. Todo, make this a variable, allow
                //  user to modify it using command line

int main()
{
    int col=0, base_col=0, entab=0;

    // Loop replacing spaces with tabs to the maximum extent
    int c=getchar();
    while( c != EOF )
    {

        // Normal state
        if( !entab )
        {

            // If whitespace goto entab state
            if( c==' ' || c=='\t' )
            {
                entab = 1;
                base_col = col;
            }

            // Else emit character
            else
                putchar(c);
        }

        // Entab state
        else
        {

            // Trim trailing whitespace
            if( c == '\n' )
            {
                entab = 0;
                putchar( '\n' );
            }

            // If not whitespace, exit entab state
            else if( c!=' ' && c!='\t' )
            {
                entab = 0;

                // Emit tabs to get close to current column position
                //  eg base_col=1, N=4, col=10
                //  base_col + 3 = 4 (1st time thru loop)
                //  base_col + 4 = 8 (2nd time thru loop)
                while( (base_col + (N-base_col%N)) <= col )
                {
                    base_col += (N-base_col%N);
                    putchar( '\t' );
                }

                // Emit spaces to close onto current column position
                // eg base_col=1, N=4, col=10
                //  base_col -> 8, and two tabs emitted above
                //  base_col + 1 = 9 (1st time thru this loop)
                //  base_col + 1 = 10 (2nd time thru this loop)
                while( (base_col + 1) <= col )
                {
                    base_col++;
                    putchar( ' ' );
                }

                // Emit buffered character after tabs and spaces
                putchar( c );
            }
        }

        // Update current column position for either state
        if( c == '\t' )
            col += (N - col%N); // eg col=1, N=4, col+=3
        else if( c == '\n' )
            col=0;
        else
            col++;

        // End loop
        c = getchar();
    }
    return 0;
}

【讨论】:

  • 主要的问题是处理 3 个空格,然后在一行的开头有一个制表符,或其他类似的交错空格和制表符序列。对于大小为 4 或更大的任何制表位,您需要从输出中省略 3 个空格。
  • @Jonathan Leffler。好点,我添加了一个说明皱纹的例子。
  • 我明白你在说什么,但我不明白如何用代码表达出来。 :(
  • 你在哪里说“... '\t' 增加 1 或更多,其他字符增加它”,你能详细说明一下吗?
  • @asdfg 我想知道我是否可以在评论中这样做;最初 col=0。我们读“a”。发出“a”,现在 col=1。读“b”。发出“b”,现在 col=2。读 ' '。进入 entab 状态,设置 base_col=2,什么都不发射,现在 col=3。读“\t”。保持在entab状态,计算新的col=8(所以在这种情况下增加了5)。读 ' '。现在 col=9 保持在 entab 状态。读“c”。退出 entab 状态,使用 base_col 和 col 计算要发出多少个制表符和空格,然后发出 '\t',' '。然后发出 'c' 并将 col 增加到 col=10。所以输入到目前为止“ab.-.c”,输出到目前为止“ab-.c”(一个空格已被消除)。明白了吗?
【解决方案2】:

我有点晚了,但这是我自己解决的方法。这是一种与上面分享的方法不同的方法,所以如果您有任何 cmets/反馈,请分享。

查看my public gist on Github 获取源代码。代码中有cmets,方法在文件顶部进行了说明,但我将在此处复制并粘贴它,以便从一开始就清楚逻辑。

方法:

  • 我们将跟踪遇到的空格数(在非制表符/非空格字符之间)

  • 我们将跟踪每个输入行的字符(不是制表符/空格/换行符)

  • 我们将通过以下方式评估空格产生的“间隙”:

    • 评估这些字符之间是否有空格数。

    • 当空格数 >= TABSIZE 时,间隙将“足够大”

    • 然后,对于“缓冲区”中的所有剩余空间,我们将分别打印出来

最后,我们打印出读入的字符(不是制表符/空白)

以及必要时更新空格数和字符数。

我仍然是一个新手程序员,所以我不确定它与此处发布的其他解决方案相比如何,但逻辑似乎更容易理解(至少对我而言)。

希望这对以后的人有所帮助!

【讨论】:

    【解决方案3】:

    我同意你的评价。用制表符替换每 n 个空格是不够的;例如,如果 n == 4,则“hi blank blank blank blank”不应替换为“hi tab”,而应替换为“hi tab blank blank”。

    听起来您需要做的是在每行阅读时跟踪当前位置,并使用此信息来确定您需要多少个标签。这有帮助吗?如果您需要更多详细信息,请告诉我!

    至于“变量与符号参数”部分,两者都绝对可行,但我可以想到使用变量的一个显着优势:您可以针对不同的 n 值运行程序,而无需重新编译。

    【讨论】:

      【解决方案4】:

      我的理解是,您不必真正知道问题是什么或如何解决问题才能回答这个问题。这个问题似乎是在问您是否了解何时使用变量而不是“符号参数”。我实际上不确定“符号参数”是什么意思;它似乎是过时的命名法。

      话虽如此,解决问题的第一部分(用制表符替换空格)相当简单。想想除法和余数。

      【讨论】:

      • 我认为这里的符号参数是#define
      【解决方案5】:

      我非常粗略地查看了您的代码,并没有什么明显的错误。

      所以我的建议是在调试器中单步执行一些输入示例,随时检查变量值,或者添加一大堆调试打印语句。无论哪种情况,您的目标都是找到程序状态开始偏离您的预期或意图的点。

      【讨论】:

        【解决方案6】:

        我目前正在耕种 KnR 并遇到了这个页面:

        Answers to Exercises

        您的练习位于:

        希望你觉得这很有用。

        真诚地, 变形

        1:http://users.powernet.co.uk/eton/kandr2/index.html“The C Programming Language”,第 2 版,Kernighan 和 Ritchie - 练习答案

        【讨论】:

        • 又一个资源。本书的一些注释按章节分为几节。这里似乎有一些好处——最重要的是:仔细阅读这本书。重要的事情被陈述了,没有继续谈论它。 eskimo.com/~scs/cclass/krnotes/top.html ;伴随 C 编程语言的注释,Kernighan 和 Ritchie (``K&R'')
        【解决方案7】:

        在上面评分最高的答案中,程序过于复杂。 为了简化这部分答案,我附上了一个更简单的代码,希望以 K&R 的风格编写(主要是通过使用 ++ 递增内联)。

        包括

        定义 TAB 4

        int main(){

        char newsentence[255],c;
        int spacecount = 0, oldsentencepointer = 0, newsentencepointer = 0;
        
        printf("Give me a sentence please:\n");
        
        while ((c = getchar()) != '\n') {
            if ((oldsentencepointer != 0) && (oldsentencepointer % TAB == 0) && (spacecount > 0))
               {
                newsentencepointer -= spacecount;         //if at tabstop, and spaces and not
                                                            first, go back to 1st space, set tab.
                newsentence[newsentencepointer++] = '\t';
                spacecount = 0;
                }
        
            if (c == ' ') {
                newsentence[newsentencepointer++] = ' ';
                spacecount++;                       //keep track of spaces before tab stop
            }
        
            else if (c == '\t') {
                newsentence[newsentencepointer++] = '\t' ;
                oldsentencepointer = TAB;   //set old pointer to TAB (does not matter if actual,
                                              only cadence important)
                continue;                   //continue from here so as not to increment 
                                              old sentence counter.
                }
        
            else {
                newsentence[newsentencepointer++] = c ;   //write whatever was old into new.
                spacecount = 0;                           //reset space counter.
                }
        
            oldsentencepointer++;
        
        }
        
        newsentence[newsentencepointer] = '\0';    //cap it off.
        
        puts(newsentence);
        
        return 0;
        

        }

        【讨论】:

          【解决方案8】:

          还有一个更简洁的解决方案,尽管它没有采用可用的最佳代码实践(滥用短路评估、通过 continue 进行笨拙的控制流、有点奇怪的“空间”循环)。

          #include <stdio.h>
          
          #define TS 8
          
          int main(int arg, char *argv[]) {
              int counter = 0, space_counter = 0, c;
              while ((c = getchar()) != EOF) {
                  ++counter;
                  if (c == ' ' && ++space_counter && (counter % TS) == 0) {
                      space_counter = 0;
                      c = '\t';
                  } else if (c == '\t') {
                      counter = space_counter = 0;
                  } else if (c != ' ') {
                      while (space_counter--)
                          putchar(' ');
                      space_counter = 0;
                      if (c == '\n')
                          counter = 0;
                  } else {
                      continue; /* don't call putchar(c) */
                  }
                  putchar(c);
              }
              return 0;
          }
          

          除空格外,读取的每个字符都是逐字打印的。而是计算空白。如果程序遇到非空白字符,它会打印与之前计算的一样多的空白,然后重置该计数器。如果遇到空白,它会通过第二个计数器(从行首/最后一个制表位开始打印的字符)检查光标是否位于制表位上。如果是,则打印一个制表符,否则只计算空白。

          输入中的一个制表符处理重置空间计数器和输出制表符,消除过程中任何多余的空白。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-06-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多