Задача из Кернигана и Ритчи:
Напишите программу, печатающую гистограмму длин слов из файла ввода.
Не нужно готовое решение. Нужны подсказки.
Напишите программу, печатающую гистограмму длин слов из файла ввода.
Не нужно готовое решение. Нужны подсказки.
khpi-iip.mipk.kharkiv.edu/library/pgm/kr/c_1.ht...
А вообще, подход очень прост. заводим int-ый массив достаточной длины (можно реализовать и динамический, но это наверно не входит в цель этой задачи), размер массива соответствует максимальной длине слова (которую можно посчитать, пройдясь по всему файлу, но, опять же, оверхед).
потом читаем файл посимвольно. Пока читаем буквы - инкрементируем счетчик, как поймали пробел, перевод строки и т.п. - инкрементируем значение ячейки массива, соответствующей длине только что прочитанного слова, счетчик длины сбрасываем.
Далее вопрос лишь в том, как лучше вывести гистограмму, ибо в Си нет встроенных возможностей рисовать графики.
Можно просто вывести построчно массив, отобразив нормированное значение каждой ячейки соответствующим количеством символов '='. Для нормировки находим самую частую длину из нашего массива и делим её на ширину "графика" (например, 60). Потом в каждой строке на экране пишем номер ячейки массива (форматируем на ширину 6 с правым выравниванием), ставим вертикальную черту, потом рисуем знаки '=' в количестве, равном значению из ячейки, разделённому на вычисленный нормировочный коэффициент.
Я видел решение этой задачи так: Считавыем файл ввода. Вместо каждого символа выводим на печать знак "=". Как только натыкаемся на пробел, прыгаем на новую строку. Должна выйти гистограмма из знаков равно. Но, где тут тогда массивы?
получится такое:
Я не пойму, зачем эти ухищрения? Почему нельзя просто вывести построчно ячейки массива, заменив каждое к-во инкрементов на знак "="? Допустим, у меня массив:
Предположим, в файле ввода два слова, одно - длиной три буквы, второе - 5 букв, результат:
1|
2|
3|=
4|
5|=
6|
7|
8|
9|
Я не пойму, зачем эти ухищрения? - это что бы весь график поместился на экране
Давайте разбираться:
В условии внешнего while функция getchar() считала литеру с экрана и запихнула её в переменную c. После этого обнулился i, в следующем while, была считана следующая литера, далее пошёл счёт сколько их до пробела после чего запихивание значения в массив.
Например для ввода: mama_\n (символ '_' - означает пробел), getchar() считает 'm', потом считает 'a' и отсчёт пойдёт начиная с 'a' (включительно). Насчитаем до пробела 3 буквы и инкрементируем значение ndigit[3].
Таким образом программа должна выдать ошибочный вывод (..., i = 3 = 1, i = 4 = 0, ...).
Ваша программа выдаёт верный вывод, но это только потому что вы "мошенничаете" ). А именно, вы для счётчика aa = 4 (aa = i + 1) выдаёте то что сидит в массиве для i = 3, таким образом ответ верный но достигается за счёт ухищрений с индексом и счётчиком, что является признаком плохого кода (труден для быстрого понимания и модификации). Не говоря уже о том что в массиве сидят неверные значения.
Проблема в том что программа "глотает" одну литеру на входе (есть ещё проблемы но о них позже. Эта главная.).
Как её решить?
Прошу прощения, описался. Должно быть: после чего инкрементация значения ячейки массива по соответствующему индексу i.
Я не знаю, как решить эту проблему. Можно какие-нибудь подсказки?
спасибо!
Пока не за что.
Я не знаю, как решить эту проблему
Да вы не теряйтесь
Если вы поняли объяснение то как решить эту проблему вам должно быть понятно.
Можно какие-нибудь подсказки?
Как подсказать ещё более прозрачно - я не знаю.
для моего первого комментария с объяснениями получается вот что:
Человек на 1-й главе K&R
То есть?
Допустим. Получим:
Как и раньше, функция getchar считала первую литеру и не запомнила её, счётчик i стал равен нулю. Далее во внутреннем while функция getchar считала вторую литеру, занесла её в переменную c, и пошёл подсчёт опять начиная со 2-й литеры. Проблему не решило.
Как сделать чтобы сначала мы проверили первую считанную и запомненную литеру, убедились что она не является пробелом, увеличили счётчик потом считали бы следующую занесли бы её и далее по циклу до пробела?
разделить чтение символа и его проверки.
оставить только один цикл, в рамках которого и делать все проверки и подсчёты.
счётчик сбрасывать тогда же, когда инкрементируется значение в массиве.
да и вообще, принцип "разделяй и властвуй" и в программировании очень полезен
Послушайте, перестаньте пожалуйста мешать.
Я прекрасно понимаю, что вы замечательный и опытный программист, решили эту задачу за несколько секунд в уме и знаете несколько различных способов её решения. Но:
Своё умение помочь вы продемонстрировали 2010-04-13 14:45 написав код с использованием разных ненужных в данном случае свойств. Когда я вам на это аргументировано указал вы отписались, что вы не знаете полноты ситуации и "суть в алгоритме". Я не спорю попусту и беспредметно поэтому я ничего не сказал в ответ.
Вы даже не дали себе труда заглянуть в поисковик и посмотреть к какой главе относится эта задача (занимает меньше одной минуты), написали очень красиво и грамотно выглядящую программу и что дальше? Вы помогли? Нет. Зачем вы это сделали?
Сейчас вы написали код с применением умных выражений, "разделяй и властвуй" (спасибо хоть про MVC не написали), который выглядит понятно новичку но неправильный (почему неправильный - потому что skip литер с инкрементацией, желательно делать через внутренний цикл а не присобачив if-ы, и почему-то инкрементируя и тут-же на следующей строке обнуляя счётчик. Это дико выглядит. Пожалуйста не отвечайте по этому поводу т.к. ваши соображения я читать не буду). Этот код неправильный и что самое опасное понятный новичку. Потому что возможно новичок ваш код поймёт и будет в дальнейшем так писать решая например задачу "убрать все лишние пробелы между строками". Вы об этом задумывались? Вряд ли. Зачем вы написали этот код?
Из этих двух промахов допущенных вами, я делаю вывод, что помочь вы в данном случае - не можете. Поэтому вторично прошу вас не мешать. В противном случае, если я увижу ещё один ваш адресуемый новичку пост - я отписываюсь от этого треда и оставляю работу помогать вам. Всего хорошего.
во-вторых, я изначально попытался описать алгоритм подсчета словами (читаем файл посимвольно. Пока читаем буквы - инкрементируем счетчик, как поймали пробел, перевод строки и т.п. - инкрементируем значение ячейки массива, соответствующей длине только что прочитанного слова, счетчик длины сбрасываем.), но возникла сложность с его реализацией. Видимо _Dimitriy не смог разделить чтение из файла и подсчет символов, из-за чего и "пропадали" символы.
Вы привели вариант кода, которые не решает проблему.
Я предложил разложить алгоритм на простейшие составляющие, которые и перечислил. Чем вреден данный подход - мне не известно.
Далее я привел код, который отражает мои предложения, хотя возможно стоило предложить блок-схему алгоритма? По идее именно с них надо начинать обучение программированию...
ps
почему неправильный - потому что skip литер с инкрементацией, желательно делать через внутренний цикл а не присобачив if-ы, и почему-то инкрементируя и тут-же на следующей строке обнуляя счётчик. - Это Ваше имхо. Вы подумали, что будет с этим вашим скипом, если последний символ будет не пробел, а перевод строки?
но всё же, мой Вам совет, не надо пытаться "сломать" алгоритм так, что бы он начал выдавать правильный результат.
Согласен.
этот подход будет выдавать неправильный результат при обнаружении нескольких пробелов подряд.
Отличная формулировка. У меня получилось более громоздко и с примером. И пробел в начале.
Теперь вы даёте хорошие, годные (проклятый луркмоар) советы. От комментариев не отписываюсь, но передаю руль вам. Судя по всему немного дожать осталось ). Только пожалуйста не пишите готовое решение, это же ничему не научит.
Да, блок-схема сейчас по-моему это overkill.
Насчёт скипа и '\n' в конце: ну я бы во 2-м while проверку на него (знаю, что 2-й раз) написал. По-моему всё-таки лучше чем ветки. Тем более что if-ы начинаются в 3-й главе.