Упражнение 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 и наткнулся на эту страницу:
Ваше упражнение находится под:
Решения
Глава 1 - Учебное пособие Введение
Пример № 21, стр. 34
Надеюсь, вы найдете это полезным.
С уважением, Morpfh
1: http://users.powernet.co.uk/eton/kandr2/index.html "Язык программирования Си", 2-е издание, Керниган и Ричи - Ответы на упражнения