Биоритмы

Else

End

Begin

Var

Const

Const

End.

Var

End.

Если Вы попытаетесь запустить эту программу на счет, то ее поведение будет зависеть от начального значения переменной IsCorrectDate. Это значение случайно, так как компилятор Турбо Паскаля не проводит начальной инициализации переменных. Скорее всего, тот байт оперативной памяти, в котором она разместится, окажется нулевым, что в Турбо Паскале расценивается как логическое значение FALSE, поэтому с большой вероятностью ничего не произойдет, и программа сразу же завершит свою работу (условие not IsCorrectDate будет выполнено). Если начальное значение IsCorrectDate окажется не нулевым, то цикл REPEAT. . .UNTIL будет выполняться до тех пор, пока Вы не выключите компьютер или не нажмете клавиши Ctrl–Break.

Будем считать, что необходимые действия осуществляются в двух процедурах с именами InputDate (ввод даты) и WriteDay (вычисление и печать дня недели). В процедуру InputDate не нужно ничего передавать из программы, так как в ней самой осуществляются ввод и контроль даты. Поэтому заголовок процедуры может иметь такой вид:

Procedure InputDate(var d,m,y: Integer; var correctly:

Boolean);

Процедура WriteDay, напротив, только получает из программы нужные ей данные и ничего не возвращает в программу, поэтому в ее заголовке параметры описываются без слова VAR:

Procedure WriteDay(d,m,у : Integer);

С учетом этого программу можно уточнить следующим образом:

IsCorrectDate: Boolean; {Признак правильной даты}

d,m,у : Integer; {Вводимая дата – день, месяц и год}

{----------------------------------------------}

Procedure InputDate(var d,m,y : Integer;

var correctly : Boolean);

{Вводит в переменные d, m и у очередную дату и проверяет ее.

Если дата правильная, устанавливает correctly=true, иначе

correctly=false }

begin {InputDate}

correctly := false

end; {InputDate}

{----------------------------------------------}

Procedure WriteDay(d,m,у: Integer);

{Вычисляет день недели и выводит его на экран}

begin {WriteDay}

end; {WriteDay}

{----------------------------------------------}

begin

repeat

InputDate(d,m,y,IsCorrectDate);

if IsCorrectDate then

WriteDay(d,m,y)

until not IsCorrectDate

Теперь можно разработать процедуру INPUTDATE. Ввод даты не вызывает трудностей – стандартные процедуры WRITE и READLN отлично приспособлены для этой цели. Для проверки правильности даты нужно проверить принадлежность месяца диапазону 1...12 и года – диапазону 1582...4903. Кроме того, число не должно выходить из диапазона 1...31. Если Вы не очень настаиваете на более точной проверке числа в зависимости от месяца и года (для февраля), то программная реализация процедуры будет следующей:

Procedure InputDate(var d,m,у : Integer;

var correctly : Boolean);

[Вводит в переменные d, m и у очередную дату и проверяет ее.

Если дата правильная, устанавливает correctly=true, иначе

correctly=false }

begin {InputDate}

Write('Введите дату в формате ДД ММ ГГГГ: ');

ReadLn(d,m,у);

correctly := (d>=1) and (d<=31) and (m>=1)

and (m<=12) and (y>=1582) and (y<=4903)

end; {InputDate}

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

Теперь разберемся с процедурой WRITEDAY. Получив в параметрах обращения день, месяц и год, она должна:

• преобразовать месяц и год так, как описано выше (год должен начинаться 1 марта);

• вычислить день недели;

• выдать на экран результат.

Первое и второе действия очень просты и легко программируются. Что касается выдачи на экран, то можно потребовать от программы, чтобы эта выдача была не просто числом от 0 до 6, а одной из строк «воскресенье», "понедельник», ..., «суббота». Для этого потребуются дополнительные усилия: нужно сначала создать массив строковых констант с именем, например, DAYS_OF_WEEK (дни_недели), а затем выбрать из этого массива и выдать на экран нужную строку. Создать массив текстовых констант можно с помощью объявления типизированной константы (см. гл. 7):

Days_of_week: array [0..6] of String [11] =

('воскресенье','понедельник','вторник',

'среда','четверг','пятница','суббота');

В этом объявлении идентификатор Days_of_week описывается в разделе констант, однако справа от него указан тип данных (массив строк), как если бы описывалась переменная, а уже только после типа стоит знак равенства и заключенный в круглые скобки список элементов массива. В результате получим следующую процедуру:

Procedure WriteDay(d,m,y : Integer);

Days_of_week: array [0..6] of String [11] =

('воскресенье','понедельник','вторник',

'среда','четверг','пятница','суббота');

c, w : Integer;

if m < 3 then

begin {Месяц январь или февраль}

m := m + 10

y := y – 1

m := m – 2; {Остальные месяцы}

c := y div 100; {Вычисляем столетие}

y := y mod 100; {Находим год в столетии}

w := abs(trunc(2.6*m–0.2)+d+y div 4+y+c div 4–2*c) mod 7;

WriteLn (Days__of_week[w])

end;

Окончательный вариант программы приведен в прил.5.1.

 

Давно известно, что творческая и физическая активность человека не остается постоянной, циклически меняется, причем периодичность ее изменения приблизительно согласуется с периодом вращения Луны вокруг Земли. Существует теория, согласно которой физическая, эмоциональная и интеллектуальная активность человека подчиняется соответствующим биоритмам. Каждый биоритм представляет собой синусоиду со строго постоянным периодом, причем для каждого биоритма существует свой период. В отдельные дни все три биоритма человека могут достигнуть своего максимума и тогда человек испытывает подъем творческих и физических сил, в такие дни у него все спорится, он легко решает проблемы, которые в другое время ему решить гораздо сложнее. Точно также существуют и «черные» дни, соответствующие спаду всех трех биоритмов.

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

Алгоритм программы можно укрупненно записать следующим образом:

• ввести дату рождения и текущую дату, проконтролировать их правильность и непротиворечивость;

• вычислить количество дней между двумя датами, чтобы определить фазу синусоид для текущей даты;

• вычислить количество дней от текущей даты до даты ближайшего пика биоритмов и даты ближайшего спада;

• определить и напечатать обе даты.

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

Procedure InputDates(vard0,m0,y0,d,m,у: Integer);

(Вводит дату рождения и текущую дату. Контролирует правильность дат и их непротиворечивость (текущая дата должна быть позже даты рождения) }

begin {InputDates}

end; {InputDates}

{-------------------------------------------------}

Procedure Get_count_of_days(d0,m0,y0,d,m,y: Integer;

Var days: Integer);

{Определяет полное количество дней, прошедших от одной даты до другой}

begin {Get_count_of_days}

end; {Get_count_of_days}

{--------------------------------------------------}

Procedure FindMaxMin(var dmin,dmax: Integer; days: Integer);

{Ищет критические дни}

begin {FindMaxMin}

end; {FindMaxMin}

{--------------------------------------------------}

Procedure WriteDates(dmin,dmax, days: Integer);

{Определяет критические даты по количеству дней, прошедших от момента рождения, и выдает эти даты на экран}

begin {WriteDates}

end; {WriteDates}

{--------------------------------------}

var

d0,d, {Дни рождения и текущий}

m0,m, {Месяцы рождения и текущий}

у0,у, {Годы рождения и текущий}

dmin, {Наименее благоприятный день}

dmax, {Наиболее благоприятный день}

days: Integer; {Количество дней от рождения}

begin {Главная программа}

InputDates(d0,m0,y0,d,m,у);

Get_numbers_of_days(d0,m0,y0, d,m, y, days) ;

FindMaxMin(dmin,dmax,days);

WriteDates(dmin,dmax, days)

end.

Начинаем детализацию программы. Прежде всего подумаем, как по двум датам вычислить разделяющее их количество дней? Если вспомнить, что следует учитывать неодинаковое количество дней по месяцам года, а также 29 февраля для високосных лет, то ответ на этот вопрос окажется не таким уж простым. Предлагаемый алгоритм подсчета количества дней заключается в вычислении количества дней от даты рождения до конца месяца, а затем и года рождения, количества дней от начала текущего года до текущего месяца и текущей даты, а также – в подсчете количества полных лет, разделяющих обе даты. Количество лет затем легко пересчитывается в количество дней с учетом длины года (365 дней для обычных и 366 дней для високосных лет). Это очень прямолинейный алгоритм, но, откровенно говоря, мне не пришло в голову ничего другого. Возможно, существует более изящный способ подсчета и Вы его знаете, тогда программная реализация будет другой.

Упростить алгоритм можно за счет создания и использования массива из 12 целых чисел, содержащего количества дней по месяцам невисокосного года, т.е. 31, 28, 31, 30 и т.д. Этот массив (назовем его SIZE_OF_MONTH – длина_месяца) можно использовать и для обратной задачи, т.е. для определения даты критических дней, а также для проверки правильности вводимых дат. Таким образом, массив SIZE_OF_MONTH будет использоваться сразу в трех процедурах. Сделаем его глобальным, для чего его описание поместим перед описанием процедур: