Прошу помощи: не запрашивает переменные

Форум для изучающих FPC и их учителей.

Модератор: Модераторы

Прошу помощи: не запрашивает переменные

Сообщение luringgleam » 07.08.2022 17:31:48

Здравствуйте!

Была бы очень признательна, если кто-то поможет разобраться.
Недавно начала изучать Паскаль по книжке Доналда Алкока "Язык Паскаль в иллюстрациях" и споткнулась на задании 2-й главы (усовершенствовать программу расчета площади для трех типов фигур - прямоугольников, треугольников и кругов, сделав, чтобы после вывода результата программа не заканчивалась, а возвращалась к решению задачи, кроме случаев, когда пользователь ввел букву "Z", которая должна стать признаком остановки).

Исходная программа из книги запрашивает первую букву соответствующего типа фигур, а потом размеры сторон для прямоугольников и треугольников, а для круга - диаметр). После чего выводится рассчитанная площадь фигуры. Ниже привожу схему программы и ее текст, который успешно компилируется, после чего программа выполняется ровно так как я и ожидала.

Схема и текст исходной программы (скан)
https://sun9-4.userapi.com/impg/CVKNOalmnj1jHTCZG4CBzn4eEj03Yld48FeXaw/AFOeL5BvGb8.jpg?size=1412x2160&quality=95&sign=60858b77419c50c84687489fe5d53431&type=album

Сам текст исходной программы
Код: Выделить всё
PROGRAM shapes(INPUT,OUTPUT);
  CONST
    pi = 3.1415926;
  VAR
    letter: CHAR; s,area,a,b,c,d: REAL; ok: BOOLEAN;
BEGIN
  ok := TRUE;
  READ(letter);
  IF (letter='П') OR (letter='п')
   THEN
     BEGIN
      READ(b,d);
      area:= b*d
     END
   ELSE IF (letter='Т') OR (letter='т')
      THEN
        BEGIN
          READ(a,b,c);
          s:= 0.5*(a+b+c);
          area:= SQRT(s*(s-a)*(s-b)*(s-c))
        END
   ELSE IF (letter='К') OR (letter='к')
         THEN
           BEGIN
             READ(d);
             area:= pi*sqr(d)/4
           END
   ELSE ok:= FALSE;
  IF ok THEN WRITE('Площадь равна',area:8:2)
      ELSE WRITE('Должно быть П, Т или К')
END.


Дальше путём продолжительных переговоров с компилятором я пришла к коду, текст которого привожу ниже. В нем слегка измененная в конце исходная программа заключена в цикл WHILE, тело которого повторяется при выполнении условия, что введенная пользователем литера не является ни 'Z', ни 'z'. Изменения кода внутри цикла коснулись части после строки "ELSE ok:= FALSE;". Полученный текст компилируется, запускается, позволяет выполнить вычисление площади для одной из фигур один раз и даже позволяет завершить программу, введя 'Z'. Но вот запрашивать повторно у пользователя значение переменной, определяющей тип фигуры программа не хочет, впрочем после двух странных итераций она что-то запрашивает, но ввод первой буквы фигуры не приводит к запросу размеров. Ниже привожу мой текст модифицированной программы и скриншоты окошка выполнения.

Текст модифицированной программы
Код: Выделить всё
PROGRAM shapes(INPUT,OUTPUT);
  CONST
    pi = 3.1415926;
  VAR
    letter: CHAR; s,area,a,b,c,d: REAL; ok: BOOLEAN;
BEGIN
  ok := TRUE;
  WRITELN('Выберите фигуру (Прямоугольник/Треугольник/Круг)');
  READ(letter);
WHILE (letter<>'Z') AND (letter<>'z')
DO
BEGIN
  IF (letter='П') OR (letter='п')
   THEN
     BEGIN
      READ(b,d);
      area:= b*d
     END
   ELSE IF (letter='Т') OR (letter='т')
      THEN
          BEGIN
            READ(a,b,c);
            s:= 0.5*(a+b+c);
            area:= SQRT(s*(s-a)*(s-b)*(s-c))
          END
   ELSE IF (letter='К') OR (letter='к')
           THEN
               BEGIN
                 READ(d);
                 area:= pi*sqr(d)/4
               END
   ELSE ok:= FALSE;
  IF ok THEN
        BEGIN
          WRITELN('Площадь равна',area:8:2);
         WRITELN('Выберите еще фигуру (или введите "Z", чтобы выйти)');
         READ(letter);
         WRITELN
        END
      ELSE
        BEGIN
          WRITELN('Должно быть П, Т или К. Попробуйте выбрать еще раз.');
          WRITELN('Или введите "Z", чтобы выйти');
          READ(letter);
          WRITELN
        END
END
END.


Попытка протестировать последовательно все типы фигур
Изображение

При первом заходе расчет площади треугольника работает
Изображение

При первом заходе расчет площади круга работает
Изображение

Как можно видеть программа успешно выполняет первый расчет, после чего без каких-либо запросов прогоняет дважды тело составного оператора последнего ELSE. Потом что-то запрашивает, но тип фигуры не опознает и понимает только "Z".

Использую компилятор Free Pascal и в качестве среды разработки - Geany.

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

Прошу прощения, если спрашиваю какие-то примитивные вещи. Я ни разу не программист. Когда-то очень давно в школе Бейсик изучали, да и то в памяти - только общие впечатления. Причем, если, глядя на содержание книжки Алкока по Паскалю я понимаю, что там рассматриваются и какие-то базовые понятия программирования, то в школе изучали только, пожалуй сам язык - на самом примитивном уровне. Говорить о том, что это было изучением программирования - не приходится... Может, только самых азов...

Помогите, пожалуйста, новичку разобраться, что происходит в модифицированной программе. - Может, какие-то соображения, предположения, в чём может быть проблема...
Аватара пользователя
luringgleam
незнакомец
 
Сообщения: 5
Зарегистрирован: 07.08.2022 13:42:38

Re: Прошу помощи: не запрашивает переменные

Сообщение zub » 08.08.2022 16:08:25

Думаю что проблема как минимум в кодировке русских букв. В utf8 они занимают несколько байт, а letter: char это только один байт, соответственно при вводе русской П (в утф8 это D09F) будет обрезано до D0 (или 9F, не проверял) и соответствующее сравнение не выполнится.

В качестве решения можно попробовать сменитьletter: char на letter: string
Или запрос на 'Выберите фигуру ((1)Прямоугольник/(2)Треугольник/(3)Круг)' и все остальное соответственно тоже. Английские буквы, цифры и "простые" символы и знаки препинания кодирутся одним байтом в utf8
zub
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Re: Прошу помощи: не запрашивает переменные

Сообщение luringgleam » 08.08.2022 23:41:51

zub писал(а):Думаю что проблема как минимум в кодировке русских букв. В utf8 они занимают несколько байт, а letter: char это только один байт, соответственно при вводе русской П (в утф8 это D09F) будет обрезано до D0 (или 9F, не проверял) и соответствующее сравнение не выполнится.

В качестве решения можно попробовать сменитьletter: char на letter: string
Или запрос на 'Выберите фигуру ((1)Прямоугольник/(2)Треугольник/(3)Круг)' и все остальное соответственно тоже. Английские буквы, цифры и "простые" символы и знаки препинания кодирутся одним байтом в utf8


Спасибо большое! Попробую. Правда все русские буквы в тексте программы, как мне казалось, должны были бы быть в кодировке CP866 как и сам текст программы (до этого в виндовую консоль вместо кириллических строк скомпилированной программой выводились стайки кракозябликов), но не исключаю, что на переменные это могло не произвести впечатление и они по какой-то причине остались в UTF8 - я eедь никак специально не объясняла компилятору, что хочу переменные в CP866... Geany, например, если ему не сказать явно, что им же ранее сохраненный текст, следует открыть именно в этой кодировке, - настойчиво пытается его прочитать как UTF...
Аватара пользователя
luringgleam
незнакомец
 
Сообщения: 5
Зарегистрирован: 07.08.2022 13:42:38

Re: Прошу помощи: не запрашивает переменные

Сообщение zub » 09.08.2022 00:08:54

Все зависит от версии компилятора и в какой кодировке сохранен исходник.
Правильный подход держать исходник в utf8 и чтоб компилятор занал об этом - либо сохранить с BOM, либо указать {$CodePage UTF8} в начале файла исходника
И соответственно быть готовым к переменной длине символов
zub
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Re: Прошу помощи: не запрашивает переменные

Сообщение luringgleam » 09.08.2022 00:11:08

Попробовала заменить на цифры - проблема явно не в этом, результат аналогичный. В общем-то и помещенные в исходном обращении скриншоты моего наивного тестирования программы демонстрировали, что запрос прекрасно справлялся и с кириллическими буквами, т.к. при первой итерации все работает как и должно.
А вот повторный запрос (изнутри тела цикла) почему-то не запрашивает вообще значение переменной letter. Возможно дело в том, что Free Pascal не воспринимает "ok" в финальной конструкции IF...THEN...ELSE... в качестве логического значения. Только как это проверить мне пока в голову не приходит. К сожалению, компилятор не помогает понять, что не так с этой последней конструкцией. Предшествующие аналогичные конструкции работают так как ожидается, а моя нет. Но FPC не отлавливает в ней никакой ошибки и выдает успешную компиляцию.

Добавлено спустя 3 минуты 39 секунд:
Программа при втором прогоне цикла исполняет обе ветви ничего не запрашивая.
Аватара пользователя
luringgleam
незнакомец
 
Сообщения: 5
Зарегистрирован: 07.08.2022 13:42:38

Re: Прошу помощи: не запрашивает переменные

Сообщение zub » 09.08.2022 00:19:41

>>проблема явно не в этом, результат аналогичный.
cp866 - такие вещи нужно оговаривать изначально, иначе вашу проблему будет не воспроизвести, я вот например скопировал и файл сохранил ессно в utf8, что привело к описаным выше проблемам.
предлагаю мух отделить от котлет. и для начала сделать вариант без кириллицы - на нем вы отладите алгоритм. потом вводите кириллицу, но не виндовс костылями, а так чтоб работало везде
zub
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Re: Прошу помощи: не запрашивает переменные

Сообщение luringgleam » 09.08.2022 00:59:30

zub писал(а):Все зависит от версии компилятора и в какой кодировке сохранен исходник...

Версия компилятора: fpc-3.2.2.i386-win32

Текст программы - в кодировке CP866 (в конце сообщения отчет о моей провальной попытке сохранить в UTF-8, избавившись от кириллицы, скомпилировать и потестировать), иначе в выводе программы не прочитать кириллические надписи.

Разве компилятору не должно быть по-барабану? - В CP866 один байт на символ, начало таблиц в обеих кодировках одинаковое (или нет?), а читать кириллицу компилятор все равно даже не пытается - символы для него - всего лишь номера в таблице. Разве нет? Он отдает эти номера виндовой консоли, а она подставляет буковки исходя из того, какая табличка символов в нее заложена создателями. Хотя... он же запрашивает, наверное, в UTF8... и получает вместо одного символа два? Можно его как-то попросить запрашивать в CP866? С другой стороны - замена на цифры ничего принципиально не изменила. Программа ведёт себя точно так же при запросе и введении цифр в качестве запрашиваемых литер. - Первая итерация работает как надо, а последующие так же как на исходных скриншотах с той разницей, что там цифры а не буквы.
И если бы проблема была именно в кодировке - почему тогда вне тела цикла программа правильно понимает кириллические буквы, вводимые пользователем?

На текущий момент вижу три проблемы: в последней конструкции IF...THEN...ELSE... выполняются обе ветви. При выполнении первой ветви вообще ничего не запрашивается... При выполнении второй что-то запрашивается. Или компилятор что-то оптимизирует и подменяет запрос из обеих ветвей одним.
Если такое возможно, то внутри цикла способ запроса "READ(letter);" начинает работать по-другому и начинает иметь проблемы с кодировкой, или не с кодировкой а еще с чем-то... Возможны еще какие-то фокусы с видимостью...

Попробую в выходные загнать в виртуалку какой-нибудь лёгкий дистрибутив Линукса, пригодный для чайников, и потестирую с вашими рекомендациями работать в UTF-8.

Интересно, в чем могут быть проблемы, вызывающие выполнение обеих ветвей конструкции в конце программы? Можно ли как-то проверить мою догадку по поводу восприятия компилятором окея?

Добавлено спустя 5 минут 54 секунды:
zub писал(а):>>cp866 - такие вещи нужно оговаривать изначально, иначе вашу проблему будет не воспроизвести, я вот например скопировал и файл сохранил ессно в utf8, что привело к описаным выше проблемам.
предлагаю мух отделить от котлет. и для начала сделать вариант без кириллицы - на нем вы отладите алгоритм. потом вводите кириллицу, но не виндовс костылями, а так чтоб работало везде

Хорошо, так и сделаю.
Вы не подскажете, что можно будет почитать, чтобы вводить кириллицу так, чтобы работало везде, в т.ч. и в Windows?
В крайнем случае буду продолжать обучение в Linux. Если это не окажется сложнее чем установка самой операционки, компилятора и среды разработки. У меня хоть и есть опыт установки этой ОС и программ на ней, но там я вообще плохо ориентируюсь. Мне бы хотелось, все же изучать Паскаль, а не администрирование новой для меня операционки...

Добавлено спустя 12 минут 3 секунды:
zub писал(а):Правильный подход держать исходник в utf8 и чтоб компилятор занал об этом - либо сохранить с BOM, либо указать {$CodePage UTF8} в начале файла исходника
И соответственно быть готовым к переменной длине символов

Сейчас попробую с {$CodePage UTF8}
А что значит "сохранить с BOM"?

Добавлено спустя 21 минуту 21 секунду:
Sorry for my clumsy English. I tried to save the English-language program in UTF-8.
В общем, при компиляции сохраненного в UTF-8 и открытого в этой кодировке текста программы результат аналогичный.

Код: Выделить всё
{$CodePage UTF8}
PROGRAM shapes(INPUT,OUTPUT);
  CONST
    pi = 3.1415926;
  VAR
    letter: CHAR; s,area,a,b,c,d: REAL; ok: BOOLEAN;
BEGIN
  ok := TRUE;
  WRITELN('Select type of shapes (Rectangle/Triangle/Circle)');
  READ(letter);
WHILE (letter<>'Z') AND (letter<>'z')
DO
BEGIN
  IF (letter='R') OR (letter='r')
   THEN
     BEGIN
      READ(b,d);
      area:= b*d
     END
   ELSE IF (letter='T') OR (letter='t')
      THEN
          BEGIN
            READ(a,b,c);
            s:= 0.5*(a+b+c);
            area:= SQRT(s*(s-a)*(s-b)*(s-c))
          END
   ELSE IF (letter='C') OR (letter='c')
           THEN
               BEGIN
                 READ(d);
                 area:= pi*sqr(d)/4
               END
   ELSE ok:= FALSE;
  IF ok THEN
        BEGIN
          WRITELN('Area is',area:8:2);
         WRITELN('Select shape again (or type "Z" to Exit)');
         READ(letter);
         WRITELN
        END
      ELSE
        BEGIN
          WRITELN('It must be R, T or C. Try select again.');
          WRITELN('Or type "Z" to Exhit');
          READ(letter);
          WRITELN
        END
END
END.


Вот краткий тест работы англоязычной версии:
Изображение

Дело не в том, что в начале ввод для одной фигуры, а дальше для другой, вот с одинаковым вводом:
Изображение
Аватара пользователя
luringgleam
незнакомец
 
Сообщения: 5
Зарегистрирован: 07.08.2022 13:42:38

Re: Прошу помощи: не запрашивает переменные

Сообщение zub » 09.08.2022 02:16:46

>>Разве компилятору не должно быть пофиг?
нет. компилятор знает в какой кодировке ожидает вывод консоль, и если он будет знать в какой кодировке исходная строка он ее может перекодировать в нужную

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

>>А что значит "сохранить с BOM"?
один из вариантов сохранения файла https://ru.wikipedia.org/wiki/%D0%9C%D0 ... 0%BE%D0%B2

>>В общем, при компиляции сохраненного в UTF-8 и открытого в этой кодировке текста программы результат аналогичный.
ну вот, возможную проблему кодировки сняли. идем дальше.
Вы используете функцию READ. тут есть нюансы.
Когда вы вводите в консоль например R и жмете ентер в буфере ввода появляется 2 символа 'R' и CR(перевод строки)
Если вы делаете READ(ПЕРЕМЕННАЯ_ТИПА_CHAR) считывает 'R' а СR остается в буфере
Если вы делаете READ(ПЕРЕМЕННАЯ_ТИПА_REAL) считывает оставшийся СR, но фильтрует его и продолжает ожидать ввода числа, и снова после ввода например 100 в буфере будет '1','0','0' и CR(перевод строки), 3 байта она преобразует в 100, но CR останется в буфере.
Таким образом несколько последоватедльных READ(ПЕРЕМЕННАЯ_ТИПА_REAL) нормально работают, но оставляют после себя СR в буфере
И во тут READ(ПЕРЕМЕННАЯ_ТИПА_CHAR) вместо ожидания ввода читает этот CR и пускает всех по ложному следу.
Самое простое - замените READ на READLN - этот вариант будет учитывать перевод строки при вводе.

чтоб программа работала нормально независимо от системы - использыейте {$CodePage UTF8} например
Код: Выделить всё
program Project1;
{$CodePage UTF8}
begin
  writeln('Привет мир!');
  ReadLn;
end.

Такая программа при сохранении исходника в utf8 будет работать правильно независимо от кодировки консоли.
Также советую использовать Lazarus и пошаговую отладку - многие вопросы снимутся сами
zub
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Re: Прошу помощи: не запрашивает переменные

Сообщение luringgleam » 09.08.2022 22:22:44

zub писал(а):...

Спасибо большое за подробные разъяснения и рекомендации!
Аватара пользователя
luringgleam
незнакомец
 
Сообщения: 5
Зарегистрирован: 07.08.2022 13:42:38


Вернуться в Обучение Free Pascal

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5

Рейтинг@Mail.ru
cron