K&R 练习 1-21 - 心理不理解

发布于 2024-09-04 19:40:43 字数 383 浏览 2 评论 0原文

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

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

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

:)

编辑:我写的代码。远可以在这里找到

The "impossible" K&R exercise.

"Write a program entab that replaces
strings of blanks by the minimum
number of tabs and blanks to achieve
the same spacing. Use the same tab
stops, say every n columns. Should n
be a variable or a symbolic
parameter?"

The problem I'm having is, I'm unsure about how to even do this correctly. I know it's not very explanatory, but that's pretty much the problem here. Most of the examples I've seen have counted a number of blanks, and replaced those series with a tab, but this isn't what its asking, I reckon I understand what its asking, but currently feel unable to do this.

Could anyone help :)

Edit: The code I've written so far can be found here.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

末骤雨初歇 2024-09-11 19:40:43

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

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

例如,每 8 个字符就有一个制表位,并将空格显示为“.”制表符为“-”;

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

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

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

编辑我查看了您的代码,我认为它比需要的更复杂。我的建议是一次只做一个角色。无需缓冲整行。在读取每个字符时保持列计数(“\n”将其重置为零,“\t”将其增加 1 或更多,其他字符将其增加)。当您看到空格(或制表符)时,不要立即发出任何内容,开始插入过程,发出零个或多个制表符,然后发出空格(在“\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;
}

If your question is "What is this asking me to do?" I think I can help by paraphrasing the original question (posing the same question in a different way).

Write a program that takes as input text with spaces and produces as output visually equivalent text using tabs to the maximum extent possible.

For example, with tabstops every 8 characters, and showing spaces as '.' and tabs as '-';

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

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

Write the program so that tabstop parameter n can be varied, i.e. allow values of n other than 8. Be prepared to justify your decision to make n a constant, or alternatively a variable.

Edit I had a look at your code and I think it is more complex than it needs to be. My advice is to do it a character at a time. There's no need to buffer a whole line. Maintain a column count as you read each character ('\n' resets it to zero, '\t' bumps it by 1 or more, other characters increment it). When you see a space (or tab), don't emit anything right away, start your entabbing process, emit zero or more tabs and then spaces later (at '\n' or a non whitespace character, whichever comes first).

A final hint is that a state machine can make this kind of algorithm a lot easier to write, validate, test and read.

Edit 2 In a shameless attempt to get the OP to accept my answer, I have now gone ahead and actually coded a solution myself, based on the hints I offered above and my comment in the discussion.

// 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;
}
戏舞 2024-09-11 19:40:43

我有点晚了,但这是我自己解决的方法。这是与上面分享的方法不同的方法,因此如果您有任何评论/反馈,请分享。

查看我在 Github 上的公开要点以获取源代码。代码中有注释,并且该方法在文件顶部进行了解释,但我将其复制并粘贴到此处,以便从一开始就清晰逻辑。

方法:

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

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

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

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

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

    • 然后,对于“缓冲区”中所有剩余的空间,我们将单独打印它们

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

如有必要,还可更新空格数和字符数。

从各种意义上来说,我仍然是一个新手程序员,所以我不确定它与此处发布的其他解决方案相比如何,但逻辑似乎更容易遵循(至少对我来说)。

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

I'm a bit late, but here's how I solved it myself. It's a different approach than what has been shared above, so please share any comments/feedback if you have any.

Check out my public gist on Github for the source code. There's comments on the code, and the approach is explained on the top of the file, but I'll copy and paste it here just so that the logic is clear from the get-go.

Approach:

  • We'll keep track of number of spaces encountered (between nontab/nonspace characters)

  • We'll keep track of characters (that aren't tabs/blanks/newlines) per input line

  • We'll evaluate the "gaps" generated by spaces by:

    • Evaluating whether the number of spaces in between those characters.

    • A gap will be "big enough" when the number of spaces is >= TABSIZE

    • Then, for all the left over spaces in our "buffer", we'll print them out individually

Finally, we print out the character that was read in (which was not a tab/blank)

As well as updating space count and character count if necessary.

I'm still a novice programmer in all senses, so I am not sure of how it would compare vs the other solutions posted in here, but the logic seems easier to follow (at least for me).

Hope this helps someone later on!

苦笑流年记忆 2024-09-11 19:40:43

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

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

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

I agree with your assessment. It won't be enough to replace every n blanks with a tab; for example, if n == 4, "hi blank blank blank blank" should not be replaced by "hi tab", but rather by "hi tab blank blank".

It sounds like what you need to do is keep track of the current position as you're reading in each line, and use this information to determine how many tabs you need. Does this help? Please let me know if you need more details!

As for the "variable vs. symbolic parameter" part, either would definitely be viable, but I can think of one significant advantage to using a variable: you can run the program for different values of n without recompiling.

摘星┃星的人 2024-09-11 19:40:43

让我提供另一个至少处理提供顶级答案的案例。但它只处理一根字符串。

假设列表是至少包含2个空格的东西。

  1. 让我们跟踪尾随空格的数量。 \
  2. 如果最后一个符号和当前符号都是空格,则可能是一个列表。 \
  3. 当尾随空格数等于 tabulation stop 时:将主计数器设置为 counter - tabulation stop - 1 (当我们使用数组时),然后将位置分配给 \t
  4. 移位计数器后变量,其他符号照常写入
/*
Write a program that takes as input text with spaces
and produces as output visually equivalent text using tabs to the maximum extent possible.

f.e. tab=4

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

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

#include <stdio.h>

#define MAXLINE 24
#define TABSTOP 4

int entab(char s[]);

int main() {
    char s[MAXLINE];

    entab(s);
    printf("%s\n", s);

    return 0;
}

int entab(char s[]) {
    int i;
    int j;
    char c;

    for (i = 0, j = 1; i < MAXLINE - 1 && ((c = getchar()) != '\n'); ++i) {
        // entab state
        if (i > 0 && s[i - 1] == ' ' && c == ' ') {
            ++j;
            if (j == TABSTOP) {
                i -= TABSTOP - 1;
                j = 1;
                s[i] = '\t';
            } else
                s[i] = c;
        }
        // Normal state
        else
            s[i] = c;
    }
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';

    return 0;
}

Let me provide another one that at least handled cases that provided top level answer. It handles one string only though.

Suppose the tabulation is the things that included at least 2 spaces.

  1. Let's track number of trailing spaces. \
  2. If the last symbol and the current symbol is space, there is could be a tabulation. \
  3. When the number of trailing spaces is equal to tabulation stop: set the main counter to counter - tabulation stop - 1 (as we work with array), then assign the position to \t
  4. After shift the counter variable, other symbols will be written as usual
/*
Write a program that takes as input text with spaces
and produces as output visually equivalent text using tabs to the maximum extent possible.

f.e. tab=4

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

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

#include <stdio.h>

#define MAXLINE 24
#define TABSTOP 4

int entab(char s[]);

int main() {
    char s[MAXLINE];

    entab(s);
    printf("%s\n", s);

    return 0;
}

int entab(char s[]) {
    int i;
    int j;
    char c;

    for (i = 0, j = 1; i < MAXLINE - 1 && ((c = getchar()) != '\n'); ++i) {
        // entab state
        if (i > 0 && s[i - 1] == ' ' && c == ' ') {
            ++j;
            if (j == TABSTOP) {
                i -= TABSTOP - 1;
                j = 1;
                s[i] = '\t';
            } else
                s[i] = c;
        }
        // Normal state
        else
            s[i] = c;
    }
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';

    return 0;
}
各自安好 2024-09-11 19:40:43

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

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

My understanding is that you don't really have to know what the problem is or how to solve it in order to answer this question. The question seems to asking whether you understand when to use variables instead of "symbolic parameters". I'm not actually sure what is meant by "symbolic parameter"; it seems to be outdated nomenclature.

Having said that, solving the first part of the question (replacing spaces with tabs) is rather straight forward. Think division and remainders.

您的好友蓝忘机已上羡 2024-09-11 19:40:43

我粗略地查看了你的代码,没有发现任何明显错误的地方。

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

I took a very cursory look at your code, and nothing is jumping out at me as blatantly wrong.

So my advice would be to either single-step through a few input examples in a debugger, examining variable values as you go, or add a whole bunch of debugging print statements. In either case, your goal is to find the point where the state of the program starts to deviate from what you expected or intended.

深陷 2024-09-11 19:40:43

我目前正在研究 KnR,并浏览了此页面:

练习答案

您的练习位于:

希望您发现这很有用。

真挚地,
Morpfh

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

I am currently plowing KnR and came across this page:

Answers to Exercises

Your exercise are located under:

Hopefully you find this useful.

Sincerely,
Morpfh

1: http://users.powernet.co.uk/eton/kandr2/index.html "The C Programming Language", 2nd edition, Kernighan and Ritchie - Answers to Exercises

濫情▎り 2024-09-11 19:40:43

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

}

In the top rated answer above, the program is overly complex.
In an attempt to simplify that part of the answer, I've attached a much simpler code hopefully written in the style of K&R (mostly by incrementing inline with ++).

include

define 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;

}

寂寞花火° 2024-09-11 19:40:43

有一个更简洁的解决方案,尽管它没有采用可用的最佳代码实践(滥用短路评估、通过 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;
}

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

输入中的制表符用于重置空格计数器并输出制表符,从而消除该过程中任何多余的空白。

There is an even more concise solution, although it does not employ the best code practices available (abusing short circuit evaluation, awkward control flow via continue, somewhat weird "space" loop).

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

Except for blanks, every character that is read is printed verbatim. Blanks are counted instead. If the program encounters a non-blank character, it prints as many blanks as it has counted before, resetting that counter afterwards. If it encounters a blank, it checks via a second counter (printed characters since the beginning of the line/last tabstop) if the cursor is on a tabstop. If it is, a tab is printed, otherwise the blank is just counted.

A tab in the input is dealt with resetting the space counter and outputting the tab, eliminating any superfluous blanks in the process.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文