Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 03.08.2011, 12:12   #1
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,323
По умолчанию Упражнения из книги Кернигана и Ритчи

Я решил перечитать книгу Кернигана и Ритчи, а попутно делать все упражнения. Следующее упражнение сделал, но, возможно, данное упражнение можно было бы сделать по-другому и проще.

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

Код:
#include <stdio.h>
#include <locale.h>

/* замена нескольких пробелов одним */
/*
Алгоритм:
ЕСЛИ считанный символ - это пробел, то увеличиваем счётчик пробелов на единицу
ИНАЧЕ (символ не пробел) проверяем счётчик пробелов больше одного.
    ЕСЛИ - да, то выводим пробел и текущий символ, а также обнуляет счётчик пробелов
    ИНАЧЕ(счетчик пробелов больше одного) ЕСЛИ предыдущий был пробелом, то выводим пробел
          выводим текущий символ
*/
int main () {
    setlocale(LC_ALL, "Russian");
    printf("Замена нескольких пробелов одним\n");

    // previousIsSpace - предыдущий символ - пробел? 0 - нет; 1 - да
    // countOfSpaces - счетчик пробелов
    int previousIsSpace = 0, c, countOfSpaces = 0;
    while ((c = getchar()) != EOF) {
        if (c == ' ') {
            previousIsSpace = 1;
            ++countOfSpaces;
        }
        else {
            if (countOfSpaces > 1) {
                putchar(' ');
                putchar(c);
                previousIsSpace = 0;
                countOfSpaces = 0;
            }
            else {
                if (previousIsSpace) {
                    putchar(' ');
                }
                putchar(c);
            }
        }
    }
    getchar();
    return 0;
}
8Observer8 вне форума Ответить с цитированием
Старый 03.08.2011, 13:09   #2
|{ot
Форумчанин
 
Аватар для |{ot
 
Регистрация: 09.03.2008
Сообщений: 127
Лампочка

Код:
while ((c = getchar()) != EOF) {
        if (c == ' ') {
            previousIsSpace = 1;
            ++countOfSpaces;
        }
        else {
            if (countOfSpaces > 1) {
                putchar(' ');
                putchar(c);
                previousIsSpace = 0;
                countOfSpaces = 0;
            }
            else {
                if (previousIsSpace) {
                    putchar(' ');
                }
                putchar(c);
            }
        }
    }
я не совсем понял зачем две переменные
Код:
countOfSpaces  и  previousIsSpace
как минимум можно одной переменной обойтись

Код:
while ((c = getchar()) != EOF) {
        if (c == ' ') {
            previousIsSpace = 1;
        
        } else {
           if (previousIsSpace) {
               putchar(' ');
               previousIsSpace = 0;
           }
         putchar(c);
        }
}
но думаю что оптимальнее будет не встраивать условие проверки

Код:
 if (previousIsSpace) {
               putchar(' ');
               previousIsSpace = 0;
           }
там где и
Код:
putchar(c);
большинство символов то не пробелы

Код:
while ((c = getchar()) != EOF) {
        if (c != ' ') {
            putchar(c);
            previousIsSpace = 0;//пусть лучше этот код повторяется
        
        } else {
           if (!previousIsSpace) {
               putchar(' ');
               previousIsSpace = 1;
           }
         
        }
}
+ еще лучше если объявить так
Код:
register int previousIsSpace = 0;
|{ot вне форума Ответить с цитированием
Старый 03.08.2011, 13:36   #3
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,323
По умолчанию

|{ot, отлично, спасибо! К этому я и стремился, но как всегда через ...

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

Упражнение 1.10 Напишите программу для копирования входного потока в выходной с заменой знаков табуляции на \t, символов возврата назад (Backspace) на \b, а обратных косых черт - на \\. Это сделает табуляцию и символы возврата легко читаемыми в потоке.

Для меня непонятна фраза выделенная красным. В консоле WinXP "Backspace" вводится Ctrl+H. Но в потоке он отсутствует, так как поток формируется, когда я нажимаю Enter (после ввода символов в консоль), если я правильно понимаю.

Код:
#include <stdio.h>
#include <locale.h>

/* замена знаков табуляции на '\t', символов возврата назад \
(Backspace) на '\b' и обратных косых черт - на \\ */
int main () {
    setlocale(LC_ALL, "Russian");

    printf("Замена знаков табуляции на \\t, символов возврата назад \
(Backspace) на \\b и обратных косых черт - на \\\\\n");

    int c;
    while ((c = getchar()) != EOF) {
        if (c == '\t') {
            printf("\\t");
        }
        else {
            if (c == '\\') {
                printf("\\\\");
            }
            else{
                // Ctrl+H
                if (c == '\b') {
                    printf("\\b");
                }
                else {
                    putchar(c);
                }
            }
        }
    }

    getchar();
    return 0;
}

Последний раз редактировалось Stilet; 04.08.2011 в 09:10.
8Observer8 вне форума Ответить с цитированием
Старый 03.08.2011, 15:39   #4
Roof
Форумчанин
 
Аватар для Roof
 
Регистрация: 01.02.2007
Сообщений: 785
По умолчанию

2 8Observer8
Цитата:
Может быть кто-нибудь знает где можно найти выполненные упражнения. Можно было бы пользоваться ими как ключом. Я не смог найти.
Это нехороший подход. Лучше выполнять самому упражнения. Они на это и рассчитаны - самостоятельная работа. Получаешь некоторый опыт самостоятельного мышления, а не просто осознание прочитанного. Этот опыт ценен.
Изо всей благодати
В руках крепко сжатых
Я донесу только капли
Roof вне форума Ответить с цитированием
Старый 03.08.2011, 16:44   #5
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,323
По умолчанию

2 Roof
Полностью согласен. Но я бы хотел выполнить упражнение и сказать себе: "Я сделал всё, что смог!". А потом подглядеть, как это сделал опытный человек. Лучше бы, конечно, Керниган и Ритчи поместили ответы в конце книги или (если им не хотелось раздувать книгу) выложили на своём сайте. И указали: "Смотри только в крайнем случае, не поддавайся соблазну!"

Упражнение 1.13 Напишите программу для вывода гистограммы длин слов во входном потоке. Построить гистограмму с горизонтальными рядами довольно легко, а вот с вертикальными столбцами труднее.

Код:
#include <stdio.h>
#include <locale.h>

#define IN 1    /* внутри слова */
#define OUT 0   /* снаружи слова */

/* гистограмма длин слов с горизонтальными рядами */
    // Алгоритм:
    // считываем по одному символу из входного потока
    // если символ - это символ пустого пространства, то мы вне слова
    //     если предыдущее состояние - это внутри слова, то выводим длину слова (в виде черт)
    // запоминаем состояние, сбрасываем счётчик длины текущего слова
    // иначе мы внутри слова
    // переключаем состояние - внутри слова, увеличиваем счётчик длины текущего
    // слова, выводим текущий символ
    // конец считывания символов
int main () {
    setlocale(LC_ALL, "Russian");

    printf("Гистограмма длин слов с горизонтальными рядами\n");

    int c, wordLength = 0;
    int state = OUT;
    // считываем по одному символу из входного потока
    while ((c = getchar())!= EOF) {
        // если символ - это символ пустого пространства, то мы вне слова
        if (c == ' ' || c == '\n' || c == '\t') {
            // если предыдущее состояние - это внутри слова, то
            // выводим длину слова (в виде черт)
            if (IN == state) {
                putchar('\n');
                for (int i = 0; i < wordLength; ++i) {
                    putchar('-');
                }
                printf("\n\n");
            }
            // запоминаем состояние, сбрасываем счётчик длины текущего слова
            state = OUT;
            wordLength = 0;
        }
        // иначе мы внутри слова
        else {
            // переключаем состояние - внутри слова, увеличиваем счётчик длины текущего
            // слова, выводим текущий символ
            state = IN;
            ++wordLength;
            putchar(c);
        }
    } // конец считывания символов


    getchar();
    return 0;
}
Программка делает следующее:
Ввод:
1234 12345 123456 123

Результат:
1234
----

12345
-----

123456
------

123
---

Правильно ли я понял задание? И программа - нормальная?

Вот как быть с гистограммой с вертикальными столбцами ума не приложу!
Если я правильно понял задание, то должно быть так:
Ввод:
1234 12345 123456 123

Результат:



Я правильно понимаю задание?

Последний раз редактировалось Stilet; 04.08.2011 в 09:11.
8Observer8 вне форума Ответить с цитированием
Старый 05.08.2011, 05:33   #6
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,323
По умолчанию

У K&R объявления переменных всегда в начале функций (блоков), а инициализация - отдельно. Это должно быть описано в стандарте ANSI C. Я искал в Draft'e - ISO/IEC 9899:201x: ссылка. Не нашёл, помогите найти!
8Observer8 вне форума Ответить с цитированием
Старый 05.08.2011, 14:09   #7
onewho
Форумчанин
 
Регистрация: 29.09.2010
Сообщений: 636
По умолчанию

В 'C' 89-го года действовало правило объявления только в начале блока.
В стандарте С99 (и С++) это правило было отменено.
onewho вне форума Ответить с цитированием
Старый 05.08.2011, 14:31   #8
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,323
По умолчанию

onewho, это радует! Спасибо!
8Observer8 вне форума Ответить с цитированием
Старый 05.08.2011, 15:15   #9
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,323
По умолчанию

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

Цитата:
Упражнение 1.20 Напишите программу detab, которая бы заменяла символы табуляции во входном потоке соответствующим количеством пробелов до следующей границы табуляции. Предположим, что табуляция имеет фиксированную ширину n столбцов. Следует ли сделать n переменной или символическим параметром?
Я экспериментально установил, что у меня в консоле TAB = 8-ми пробелам и каждый '\t' я заменяю на 8 пробелов. получается слишком уж просто! И параметр n, кажется очевидным, нужно сделать константой. У меня n: NSPACES = 8. Или я всё же правильно понял, что имели ввиду авторы.

Код:
#include <stdio.h>
#include <locale.h>

#define NSPACES 8

/* замена табуляций определённым количеством пробелов */
int main () {
    setlocale(LC_ALL, "Russian");

    printf("Замена табуляций определённым количеством пробелов\n");
    int c;
    while ((c = getchar()) != EOF) {
        if (c == '\t') {
            for (int i = 0; i < NSPACES; ++i) {
                printf(" ");
            }
        }
        else {
            putchar(c);
        }
    }
    return 0;
}

Последний раз редактировалось 8Observer8; 05.08.2011 в 15:17.
8Observer8 вне форума Ответить с цитированием
Старый 04.11.2011, 21:13   #10
SlivTime
Новичок
Джуниор
 
Регистрация: 04.11.2011
Сообщений: 2
По умолчанию

Насчет задания 1.13. Я сделал следующим образом:

Код:
#include <stdio.h>

//Вывод вертикальной гистограммы длин слов

main()
{
	int i, j, c, lenght, MaxLenght, MaxValue;
	int WordsLenght[30];

	MaxLenght = MaxValue = lenght = 0;
	for ( i = 0; i < 30; ++i)
		WordsLenght[i] = 0;

	while ((c = getchar()) != EOF) {  			 
		if (c == ' ' || c == '\n' || c == '\t') {
			++WordsLenght[lenght];
			lenght = 0;
		}
		else 
			++lenght;
	}

	//Вычисление границ гистограммы
	for ( i = 0; i < 30; ++i) {
		if ( MaxValue < WordsLenght[i] )
			MaxValue = WordsLenght[i];
		if ( WordsLenght[i] != 0 )
			MaxLenght = i;
	}

	//Вывод результата
	printf("\n");
	for ( i = MaxValue; i > -1; --i ) {
		if ( i > 0) 
			printf("%2d ", i);
		else if ( i == 0 ) 
			printf("   ");
		for ( j = 1; j < MaxLenght+1; j++ ) {
			if ( i > WordsLenght[j] )
				putchar(' '), putchar(' '), putchar(' ');
			else if ( i == 0) 
				printf("%3d", j);
			else {
				putchar(177), putchar(177), putchar(177);
			}
		}
		printf("\n");
	}
}
В итоге имеем "резиновую" гистограмму, подстраивающуюся под конкретный входной поток. С Горизонтальной гистограммой значительно проще, если нужно, выложу код.
Как мне кажется, определять внутри или снаружи слова мы в данный момент находимся, здесь ни к чему. Или я не прав?

Вывод получается следующим:

Код:
12       ±±±                                                               
11 ±±±   ±±±                                                               
10 ±±±   ±±±   ±±±±±±                                                      
 9 ±±±   ±±±   ±±±±±±                                                      
 8 ±±±   ±±±   ±±±±±±                                                      
 7 ±±±   ±±±   ±±±±±±                                                      
 6 ±±±   ±±±   ±±±±±±±±±                                                   
 5 ±±±±±±±±±±±±±±±±±±±±±                                                   
 4 ±±±±±±±±±±±±±±±±±±±±±   ±±±                                             
 3 ±±±±±±±±±±±±±±±±±±±±±   ±±±                                             
 2 ±±±±±±±±±±±±±±±±±±±±±±±±±±±                                             
 1 ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±      ±±±            ±±±               ±±±
     1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Единственное, что мне не нравится в таком выводе - то, что числа 1-9 выравниваются по правой границе (__X), а хотелось бы примерно так (_X_). В голову приходят только различные костыли. Может, кто-нибудь подскажет нормальное решение?

Последний раз редактировалось SlivTime; 04.11.2011 в 21:22. Причина: Дополнение
SlivTime вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Упражнения c# veter48 Помощь студентам 0 12.07.2011 18:53
[Си] Упражнения Fobo5 Помощь студентам 1 02.02.2011 21:22
(С) Простое упражнение из Кернигана, Ричи. Пробелы Матвейка Помощь студентам 1 07.06.2009 12:37
Упражнения делфи MAKEDON Свободное общение 1 26.08.2008 02:31