LINUX.ORG.RU

Лишний writeln в Pascal?

 


2

1

Добрый день!

Занимаюсь решением задач из задачника Андрея Столярова. В процессе решения задачи 2.15. возник вопрос.

Задача следующая:

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

*******
     *
    *
******* *******
  *          *
 *          *
******* ******* *******
          *          *
         *          *
        ******* *******
                  *
                 *
                *******

Программу написал следующую:

rogram z_sequence;

procedure PrintChars(count: integer);
var
    i: integer;
begin
    for i := 1 to count do
        write('*')
end;

procedure PrintSpaces(count: integer);
var
    i: integer;
begin
    for i := 1 to count do
        write(' ')
end;

procedure PrintLineOfDiamondUp(i, z: integer);
begin
    if i = 1 then
        PrintChars(z)
    else
        begin
            PrintSpaces(z - i);
            PrintChars(1)
        end;
    writeln
end;

procedure PrintLineOfDiamondDown(i, z: integer);
begin
    if i = 1 then
        exit
    else
        begin
            PrintSpaces(i - 1);
            PrintChars(1)
        end
end;

procedure PrintFirstHalf(z: integer);
var
    i: integer;
begin
    for i := 1 to (z div 2) do
        PrintLineOfDiamondUp(i, z)
end;

procedure PrintFirstCenterBar(z, n: integer);
begin
    if n > 1 then
        begin
            PrintChars(z);
            PrintSpaces(1);
            PrintChars(z);
            writeln
        end
    else
        PrintChars(z);
        writeln
end;

procedure PrintHalf(i, a, z: integer);
var
    count: integer;
begin
    if i = 1 then
    begin
        for count := (z div 2) downto 2 do
            begin
                PrintSpaces(a * z + a);
                PrintLineOfDiamondDown(count, z);
                writeln
            end
    end;
    if i > 1 then
    begin
        for count := (z div 2) downto 2 do
            begin
                PrintSpaces(a * z + a);
                PrintLineOfDiamondDown(count, z);
                PrintSpaces((z div 2 + 1) + 1 + (z - 2));
                PrintChars(1);
                writeln
            end
    end
end;

procedure PrintBar(i, a, z: integer);
begin
    if i = 1 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        writeln
    end;
    if i = 2 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end;
    if i >= 3 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end
end;

var
    i, n, a, z: integer;
begin
    repeat
        writeln('Enter the letter''s height (5 or more, odd)');
        readln(z);
    until (z >= 5) and (z mod 2 = 1);
    repeat
        writeln('Enter the number of the letters');
        readln(n);
    until (n >= 1);
    PrintFirstHalf(z);
    PrintFirstCenterBar(z, n);
    a := 0;
    for i := n downto 1 do
        begin
            PrintHalf(i, a, z);
            PrintBar(i, a, z);
            a := a + 1
        end
end.

Однако, она выдавала такой результат:

*******
     *
    *
******* *******

  *          *
 *          *
******* ******* *******
          *          *
         *          *
        ******* *******
                  *
                 *
                *******

т.е. после первой центральной черты печатается лишний перенос строки.

Опытным путем понял, что если в процедуре PrintFirstCenterBar убрать writeln и напечатать вместо

procedure PrintFirstCenterBar(z, n: integer);
begin
    if n > 1 then
        begin
            PrintChars(z);
            PrintSpaces(1);
            PrintChars(z);
            writeln
        end
    else
        PrintChars(z);
        writeln
end;
procedure PrintFirstCenterBar(z, n: integer);
begin
    if n > 1 then
        begin
            PrintChars(z);
            PrintSpaces(1);
            PrintChars(z);
        end
    else
        PrintChars(z);
        writeln
end;

программа станет работать корректно.

Но почему так происходит?

Ни PrintChars ни PrintSpaces не печатает перенос строки. В условии после else стоит writeln и если попросить программу напечатать одну букву Z лишнего переноса не произойдет.

Подпрограмма PrintBar

procedure PrintBar(i, a, z: integer);
begin
    if i = 1 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        writeln
    end;
    if i = 2 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end;
    if i >= 3 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end
end;

очень похожа по своей структуре на PrintFirstCenterBar, но там все writeln стоят на своём месте и никакого лишнего переноса строки не вызывают.

Так почему же тогда в подпрограмме PrintFirstCenterBar оператор writeln является лишним?



Последнее исправление: KOMMUNIST90 (всего исправлений: 1)

procedure PrintFirstCenterBar(z, n: integer);
begin
    if n > 1 then
        begin
            PrintChars(z);
            PrintSpaces(1);
            PrintChars(z);
        end
    else
        PrintChars(z);
        writeln
end;

Судя по форматированию, после else должен быть begin...end.


    else
    begin
        PrintChars(z);
        writeln
    end;
end;
akk ★★★★★
()
Ответ на: комментарий от akk

Спасибо огромное, все теперь работает корректно! А то я себе весь моск сломал, а этот косяк так и не заметил :(

KOMMUNIST90
() автор топика

Опытным путем понял, что если в процедуре PrintFirstCenterBar убрать writeln и напечатать вместо

Надо было не опытным путём убирать строчки, а добавить отладочные логи. Вот так:

procedure PrintFirstCenterBar(z, n: integer);
begin
write('(A)');
    if n > 1 then
        begin
write('(B)');
            PrintChars(z);
            PrintSpaces(1);
            PrintChars(z);
write('(C)');
            writeln
write('(D)');
        end
    else
        PrintChars(z);
        writeln
write('(E)');
end;

И ты бы сразу, увидев лишний перенос строки между (D) и (E), понял в чём дело. Но вообще, такие вещи надо уметь в уме делать: мысленно вместо компилятора и компа выполнять построчно исходник программы (строго соблюдая синтаксические конструкции языка). Так бы ты тоже сразу понял в чём дело.

firkax ★★★★★
()

А ещё (не по проблеме, но крайне полезно для обучения), я как вижу циклы ты уже умеешь. Но зачем писать вот такое тогда?

    if i = 1 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        writeln
    end;
    if i = 2 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end;
    if i >= 3 then
    begin

Ты же понимаешь что этот код сломается когда i будет равно 4, да и вообще, сколько бы ты ни дописывал туда блоков, всё равно все значения i не учесть а код становится огромным и с кучей бесполезных повторов одного и того же?

firkax ★★★★★
()
Ответ на: комментарий от firkax

Ты же понимаешь что этот код сломается когда i будет равно 4

Я особо сильно не вдавался в конкретику, но фундаментальной проблемы в подходе

if (condition1) {
   // обрабатываем особый случай #1
}
else if (condition2) {
   // обрабатываем особый случай #2
}
else {
   // обрабатываем всё остальное
}

я не вижу. Вы бы не могли развернуть свою мысль? Там действительно «всё остальное» неправильно работает?

bugfixer ★★★★
()
Ответ на: комментарий от bugfixer

А, тфу, немного перепутал. Код не сломается т.к. больше 3 там быть не может. Но в if-ах у него не «особые случаи» а однотипные - развёрнутый на разное количество итераций один и тот же цикл. Соответственно можно было сделать просто цикл, без if-ов.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от firkax

Во-первых, у него нет else для «всего остального».

Ну, тогда беру свои слова обратно ;) Тем не менее приятно что подрастающее поколение пытается что-то самостоятельно одолеть - это позитивненько («я щитаю»).

bugfixer ★★★★
()

а на букву «V» вы как собираетесь ЭТО портировать ?

hint: делаете битовый(фик с ним, байтовый) массив X*Y чтобы влезло всё отображение. Заполняете спокойно и выводите построчно

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Ну вот, ленивые кодеры заменяют эффективное векторное описание нубской растровой картинкой.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Спасибо большое!

Переписал процедуру PrintBar с

procedure PrintBar(i, a, z: integer); 
begin
    if i = 1 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        writeln
    end;
    if i = 2 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end;
    if i >= 3 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end
end;

на

procedure PrintBar(i, a, z: integer); 
var
    c: integer = 0; 
begin
    PrintSpaces(a * z + a);
    repeat 
        PrintChars(z);
        PrintSpaces(1);
        c := c + 1
    until (c = i) or (c = 3);
    writeln
end;

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

KOMMUNIST90
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.