Упражнение K&R 1-21 - Психическое непонимание

"Невозможное" упражнение K&R.

"Напишите программу entab, которая заменяет строки пробелов минимальным количеством вкладок и пробелов для достижения одинакового интервала. Используйте одинаковые остановки табуляции, скажем, каждые n столбцов. Должно ли n быть переменной или символическим параметром?"

У меня проблема в том, что я не уверен, как это сделать правильно. Я знаю, что это не очень объясняет, но в этом и заключается проблема. Большинство примеров, которые я видел, подсчитали количество пробелов и заменили эти серии на вкладку, но это не то, что он просит, я думаю, я понимаю, о чем он просит, но в настоящее время чувствую, что не могу этого сделать.

Может ли кто-нибудь помочь:)

Изменить: код, который я написал до сих пор можно найти здесь.

8 ответов

Если ваш вопрос "Что это, просит меня сделать?" Я думаю, что могу помочь, перефразируя исходный вопрос (излагая тот же вопрос другим способом).

Напишите программу, которая принимает в качестве входного текста пробелы и выдает в качестве выходного визуально эквивалентный текст с использованием вкладок в максимально возможной степени.

Например, с табуляции каждые 8 ​​символов и с пробелами в виде "." и вкладки как '-';

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

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

Напишите программу так, чтобы параметр табуляции n можно было варьировать, т. Е. Разрешать значения n, отличные от 8. Будьте готовы обосновать свое решение сделать переменную na или, в качестве альтернативы, переменной.

Редактировать Я посмотрел на ваш код, и я думаю, что он более сложный, чем должен быть. Мой совет - делать это персонажем одновременно. Нет необходимости буферизовать целую строку. Сохраняйте количество столбцов при чтении каждого символа ('\n' сбрасывает его в ноль, '\t' увеличивает его на 1 или более, другие символы увеличивают его). Когда вы видите пробел (или табуляцию), не запускайте ничего сразу, запустите процесс entabbing, выбросьте ноль или более табуляций, а затем пробелы позже (в "\ n" или в непропуске, в зависимости от того, что произойдет раньше).

И последний совет: конечный автомат может значительно облегчить написание, проверку, тестирование и чтение такого алгоритма.

Редактировать 2 В бесстыдной попытке заставить ОП принять мой ответ, я теперь пошел дальше и фактически сам кодировал решение, основываясь на подсказках, которые я предложил выше, и на моем комментарии в обсуждении.

// K&R Exercise 1-21, entab program, for Stackru.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;
}

Я немного опоздал, но вот как я решил это сам. Это другой подход, чем тот, о котором говорилось выше, поэтому, пожалуйста, поделитесь любыми комментариями / отзывами, если таковые имеются

Проверьте мой публичный гист на Github для исходного кода. Есть комментарии к коду, и подход объяснен в верхней части файла, но я скопирую и вставлю его здесь просто для того, чтобы логика была ясна с самого начала.

Подход:

  • Мы будем следить за количеством встреченных пробелов (между символами не / пробел)

  • Мы будем отслеживать символы (которые не являются символами табуляции / пробелов / новых строк) для каждой строки ввода

  • Мы оценим "пробелы", сгенерированные пробелами:

    • Оценка, является ли количество пробелов между этими символами.

    • Пробел будет "достаточно большим", если число пробелов>= TABSIZE

    • Затем для всех оставшихся пробелов в нашем "буфере" мы распечатаем их по отдельности.

Наконец, мы распечатываем символ, который был прочитан (который не был табулатурой)

А также обновление количества пробелов и знаков, если это необходимо.

Я все еще начинающий программист во всех смыслах, поэтому я не уверен, как он будет сравниваться с другими решениями, опубликованными здесь, но логика, кажется, легче следовать (по крайней мере, для меня).

Надеюсь, это поможет кому-то позже!

Я согласен с вашей оценкой. Недостаточно заменить каждые n пробелов вкладкой; например, если n == 4, "hi blank blank blank blank" следует заменить не на "hi tab", а на "hi tab blank blank".

Похоже, вам нужно отслеживать текущую позицию во время чтения каждой строки и использовать эту информацию, чтобы определить, сколько вкладок вам нужно. Это помогает? Пожалуйста, дайте мне знать, если вам нужно больше деталей!

Что касается части "переменная в сравнении с символическим параметром", то любая из них определенно будет жизнеспособной, но я могу подумать об одном существенном преимуществе использования переменной: вы можете запустить программу для разных значений n без перекомпиляции.

Я очень бегло посмотрел на твой код, и ничто не показалось мне таким грубым и неправильным.

Поэтому я бы посоветовал либо пошагово просмотреть несколько входных примеров в отладчике, исследуя значения переменных по ходу работы, либо добавить целую кучу отладочных операторов печати. В любом случае ваша цель состоит в том, чтобы найти точку, в которой состояние программы начинает отклоняться от того, что вы ожидали или намеревались.

Насколько я понимаю, вам не обязательно знать, в чем заключается проблема или как ее решить, чтобы ответить на этот вопрос. Кажется, возникает вопрос: понимаете ли вы, когда использовать переменные вместо "символических параметров". Я не совсем уверен, что подразумевается под "символическим параметром"; похоже, устаревшая номенклатура.

Сказав это, решение первой части вопроса (замена пробелов табуляцией) довольно простое. Подумайте о разделении и остатках.

Существует еще более сжатое решение, хотя оно не использует лучшие доступные методы кодирования (злоупотребление оценкой короткого замыкания, неуклюжий поток управления с помощью продолжения, несколько странный "пробел").

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

За исключением пробелов, каждый читаемый символ печатается дословно. Бланки засчитываются вместо. Если программа встречает непустой символ, она печатает столько пробелов, сколько насчитала до этого, а затем сбрасывает этот счетчик. Если он встречает пробел, он проверяет с помощью второго счетчика (напечатанные символы с начала строки / последней табуляции), находится ли курсор на табуляции. Если это так, вкладка печатается, в противном случае пустое просто считается.

Вкладка на входе имеет дело со сбросом счетчика пробелов и выводом вкладки, устраняя любые лишние пробелы в процессе.

В ответе с самым высоким рейтингом выше, программа является слишком сложной. В попытке упростить эту часть ответа я приложил гораздо более простой код, который, как мы надеемся, был написан в стиле K&R (в основном, путем увеличения на строку с ++).

включают

определить таблицу 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;

}

Я сейчас пашу KnR и наткнулся на эту страницу:

Ответы на упражнения

Ваше упражнение находится под:

Надеюсь, вы найдете это полезным.

С уважением, Morpfh

1: http://users.powernet.co.uk/eton/kandr2/index.html "Язык программирования Си", 2-е издание, Керниган и Ричи - Ответы на упражнения

Другие вопросы по тегам