K& R练习1-21 - 精神上的不理解

时间:2010-06-11 03:34:36

标签: c kr-c

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

  

“编写替换的程序entab   最小的空白串   要实现的选项卡和空白的数量   相同的间距。使用相同的选项卡   停止,说每n列。应该n   是变量还是象征性的   参数?“

我遇到的问题是,我不确定如何正确地做到这一点。我知道这不是很解释,但这就是问题所在。我见过的大多数例子都计算了一些空白,用一个标签取代了那些系列,但这不是它的要求,我想我明白它的要求,但目前感觉不能这样做。

任何人都可以提供帮助:)

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

8 个答案:

答案 0 :(得分:15)

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

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

例如,每隔8个字符使用tabstops,并将空格显示为“。”和标签为' - ';

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

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

编写程序,以便tabstop参数n可以改变,即允许n以外的n值。准备好证明你决定使n成为常数,或者变量。

编辑我查看了您的代码,我认为它比它需要的更复杂。我的建议是一次做一个角色。没有必要缓冲整行。在读取每个字符时保持列数('\ n'将其重置为零,'\ t'将其重置为1或更多,其他字符将其增加)。当你看到一个空格(或标签)时,不要立即发出任何东西,开始你的诱捕过程,发出零个或多个标签,然后再空格(在'\ n'或非空格字符,以先到者为准)。 / p>

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

编辑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;
}

答案 1 :(得分:1)

我同意你的评估。用标签替换每个n个空白是不够的;例如,如果n == 4,“hi blank blank blank blank”不应该被“hi tab”替换,而应该被“hi tab blank blank”替换。

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

对于“变量与符号参数”部分,要么肯定是可行的,但我可以想到使用变量的一个显着优点:您可以在不重新编译的情况下为不同的n值运行程序。

答案 2 :(得分:1)

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

查看bheklilr's answer以获取源代码。有关于代码的注释,并且该方法在文件的顶部进行了解释,但我会将其复制并粘贴到此处,以便从一开始就清楚逻辑。

  

方法:

     
      
  • 我们会跟踪遇到的空格数量(非空格/非空格字符之间)

  •   
  • 我们会跟踪每个输入行的字符(不是标签/空白/换行符)

  •   
  • 我们将评估&#34;差距&#34;由空间生成:

         
        
    • 评估这些字符之间的空格数。

    •   
    • 差距足够大&#34;当空格数为> = TABSIZE

    • 时   
    • 然后,对于我们&#34;缓冲区&#34;中的所有剩余空格,我们将单独打印出来

    •   
  •   
     

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

     

如果需要,还可以更新空间计数和字符数。

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

希望以后可以帮助别人!

答案 3 :(得分:0)

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

话虽如此,解决问题的第一部分(用制表符替换空格)是相当直接的。想想分裂和剩余。

答案 4 :(得分:0)

我粗略地看了一下你的代码,没有任何东西像我公然的错误一样跳出来。

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

答案 5 :(得分:0)

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

Answers to Exercises

您的锻炼位于:

希望您觉得这很有用。

此致 Morpfh

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

答案 6 :(得分:0)

在上面评分最高的答案中,该程序过于复杂。 为了简化答案的这一部分,我附上了一个更简单的代码,希望用K&amp; 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;

}

答案 7 :(得分:0)

有一个更简洁的解决方案,虽然它没有采用可用的最佳代码实践(滥用短路评估,通过继续进行笨拙的控制流程,有点奇怪&#34;空间&#34;循环)。

#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;
}

除了空白之外,每个读取的字符都是逐字打印的。空白被计算在内。如果程序遇到非空白字符,则会打印与之前计数的空白一样多的空白,之后重置该计数器。如果它遇到空白,它将通过第二个计数器(自行/最后一个tabstop开始以来打印的字符)进行检查(如果光标位于tabstop上)。如果是,则打印标签,否则只计算空白。

输入中的选项卡处理重置空间计数器并输出选项卡,从而消除过程中任何多余的空白。