Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование для начинающих #14


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

Программирование для начинающих

Выпуск 14

4 MAR 2001

 
 
 
Ведущий рассылки: Вячеслав Мацнев
e-mail: stac@stacmv.net
Здравствуйте!

В этом выпуске читайте:

ОТСЕБЯТИНА

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

Хэй, только в прошлом выпуске я сказал, что перестал использовать Windows 95, как Microsoft объявила о прекращении поддержки этой системы. Вот, видите, что значит правильно выбрать момент.

Теперь обсудим нашу с вами активность. Где она, спрашивается? Ну я-то ладно, у меня к Инету доступа нет :(, а у вас что, тоже? :( Активность сильно снизилась, такие дела. ДЗ делать никто почти не торопится. Может оно не надо? Нет, действительно, может же такое быть. Я тут придумал ДЗ, а может оно плохое? Может такое ДЗ не нужно никому? Если что, просто намекните, а то я, ведь, своим умом не дойду до этого никогда.

Кстати, насчет вторго ДЗ по Бейсику. Подписчица, скрывающаяся :) под ником2 sinoptik, единственная, кто прислал ряд выполненых задачек (из которых я засчитал не все, понятно). Остальные, видимо решили, что это была шутка. Друзья, до 1 апреля еще почти месяц. Или просто меня уже не читает никто. Ну я же говорю, доступа к Сети не имею, не могу статистику посмотреть. О, идея! У меня тут полно идей рождается, прям родильный дом какой-то, только, вот, до реализации мало какая идея доходит. Но это, кстати, нормально. Знаете, разработка нового продукта (не важно какого) начинается с поиска идей. Грубо говоря, куча людей в размере двух человек :) организует фирму, арендует офис и, имитируя рабочую атмосферу, сидит и думает, чем фирма будет заниматься. Такие конторы называются "серыми мышками" (не помною, кто придумал эту классификацию, но там еще присутствуют "львы","лисы" и "ласточки"). Это ларьки, всяческие палатки с киосками, частные пекарни т.д. К чему это я? А просто так. Речь идет об инновационном процессе (я скромно полагаю, что рассылка наша уникальна в своем роде, новое слово в.. и т.п.). Так вот, любой инновационный процесс начинается с поиска людей, т.е. идей (опечатался). Идей обычно находится прорва, и есть статистика, что лишь 5% из них доходят до реализации и только 20% из эих пяти процентов (т.е 1% от общего числа идей) имеют успех. С десяток моих идей уже провалились в... очень далеко, но у меня есть еще.

Вот очередная: пусть один из вас сходит на Subscribe.ru и возьмет там файл со статистикой рассылки, т.е. с динамикой подписки (не помню точно, как это называется, но есть такая страничка у каждой рассылки, где указано сколько человек в какой день подписалось или отписалось). Возьмет и пришлет мне. Затем мы напишем программу, которая будет по данным из этого файла строить графики. Сможем мы это сделать или нет?

Если кто-то имеет другие предложения ... Я тут просил вас в 12-ом выпуске (полагаю не все его получили в читабельном виде :( ... бывает) поднапрячь фантазию, но что-то никто не соизволил.

Итак, сходить на Subscribe.ru за указанным файлом желает... prog59. Отлично, люблю, когда есть добровольцы :).

Ок. Пока все. Вспомню еще что-нибудь - скажу.

ТЕОРИЯ

Так уж устроена наша жизнь, но мы часто совершаем разные действия, постоянно их повторяя. К примеру, нарезая хлеб, мы повторяем одинаковые, по сути, действия для отрезки каждого отдельного куска. Это есть цикл (если кто не знает, что означает слово "цикл", обратитесь к энциклопедии). Циклы бывают разные (даже очень), но мы будем рассматривать циклы, как совокупность повторяющихся действий.

Цикл характеризуется таким параметром, как число повторений. Это число может быть известно заранее или нет. Положим, к обеду вы должны отрезать себе два куска батона. Т.е. резать хлеб надо два раза (или один, если просто разрезать батон пополам). Число повторений известно. Другой случай - надо порезать тот же батон (весь). Число кусков не задано, сколько получится, столько и получится.

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

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

-жать кнопку вызова лифта, пока она не загорится-
-до тех пор, пока двери лифта не откроются стоять на месте (плясать, рисовать на стене матерное слово и пр.)-

Усмотреть повторяющиеся действия в отрезании кусков батона легко, а вот, все ли из вас видят повторяющиеся действия в последних двух примерах? У тех, кто уже делал домашнее задание проблем возникать не должно. Например, в задаче basic/1/6 используется такой цикл: до тех пор, пока любая клавиша не будет нажата ничего не делать.

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

С помощью таких циклов организуется пауза в программах. Здесь для выхода из цикла может потребоваться нажатие пользователем какой-нибудь конкретной либо любой кнопки или наступление какого-то события.

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

Вот, парень пошел на свидание. Положим местом встречи является площадь с памятником посредине. Парень действует по программе, в которй есть такой фрагмент: обходить вокруг памятника ПОКА девушка не придет. В принципе, такое поведение логично, особенно если не известно с какой стороны появится девушка (однажды я был в похожей ситуации, правда посмотреть, что находится на другой стороне площади за памятником, я додумался только минут через 15 :). Мысленно представьте себе действия парня (когда пишите программу, всегда мысленно представляйте, как она будет работать). Приходит парень на пощадь. Обходит вокруг памятника. Проверяется уловие, на месте ли девушка. Еще не пришла. Ок, парень еще раз обходит вокруг памятника. Появилась ли девушка? Да, вон она стоит. Ок. Парень, в соответствии с программой вечера, переходит к активным действиям.

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

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

Кстати, если исправление в программу парня не вносить, и если девушка-таки уйдет, то парень, в соответстие с программой, будет ходить вокруг памятника до потери сознания (именно такие явления лежат в основе зависаний компьютера).

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

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

По такой схеме функционируют многие программы, в том числе операционные системы (попробуйте, например, загрузиться в DOS, а потом выйти из нее) и живые организмы, типа нас с вами.

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

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

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

Если парень и девушка выйдут на площадь оба с одной стороны, то рассмотренный выше алгоритм действий парня будет работать нормально. А если они появятся с разных сторон, то случится такая вот беда:

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

Надо снова пропатчить программу парня. Давайте, он будет обходить не целый круг, а пол круга вокруг памятника: ПОКА нет девушки перейти на противоположную сторону площади, обойдя памятник.

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

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

Как вы, наверное, уже давно сообразили, нужно встроить проверку наличия девушки в саму процедуру обхода вокруг памятника, т.е. в тело цикла. Т.е. что-то типа: ПОКА не встретилась девушка сделать шаг. Это, конечно, упрощенно. Представьте-ка себе, как работает ваш мозг, выполняя миллионы проверок кучи условий постоянно. Надо сказать, что программы для компьютеров тоже довольно сложны, в том смысле, что программист должен учитывать очень много факторов и проверять много условий.

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

Хотя я и отвлекся от циклов, но, надеюсь, не зря. Хочу показать, что вам необходимо научиться предусматривать большое число вариантов(в идеале все) развития ситуации (хода выполнения вашей программы). С этой (в частности) целью я открываю новую рубрику СТРАТЕГИЯ И ТАКТИКА ПРОГРАММИРОВАНИЯ (название пока временное), где мы будем обсуждать "секреты и хитрости". В кавычках, потому что для опытных программистов это никакие не секреты, а вот для начинающих они могут таковыми являться.

А чтобы закончить с теорией циклов, подведем некоторые итоги.

Цикл - это совокупность повторяющихся операторов программы (действий).

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

Число повторений может также быть не известным заранее и определяться условием выхода из цикла. Такой цикл называется циклом с неизвестным заранее числом повторений.

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

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

В других языках программирования вам могут встретиться и другие виды циклов.

СТРАТЕГИЯ И ТАКТИКА

Все предусмотреть невозможно. Но надо стараться.

В том числе программист должен предусмотреть все возможные ситуации, которые могут произойти при выполнении его программы.

Пример, задача basic/1/5. Как вы помните, смысл задачи состоит в том, чтобы написать программу для решения квадратных уравнений. Я не требовал от вас учета всех возможных ситуаций при написании программы, мне было важно проверить как вы разобрались с IF. Но, тем не менее...

Рассмотрим простейший вариант программы:

PRINT "Введите коэффициенты уравнения a*x^2+b*x+c=0"
INPUT a,b,c
d=b^2-4*a*c
IF d<0 THEN
  PRINT "Корней нет"
  END
END IF
IF d=0 THEN
  PRINT "X=";-b/(2*a)
  END
END IF
IF d>0 THEN
  PRINT "X1=";(-b+SQR(d))/(2*a)
  PRINT "X2=";(-b-SQR(d))/(2*a)
  END
END IF

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

Но все программы, присланные подписчиками, очень похожи на этот, и это тоже не удивительно, так как на момент выдачи домашнего задания basic/1, других способов решения этой задачи вы еще не знали. Теперь знаете. Тем более интересно будет для нас сравнить два варианта "одной и той же" программы - приведенного выше и того, к которому мы придем исправив ошибки и недочеты первого.

Недочет номер раз. Где название программы? Очень неплохо бывает сообщить пользователю при запуске программы ее название и функциональное назначение, если название это не поясняет.

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

Вспомните программы, которыми вы пользуетесь: у каждой из них присутствует возможность выхода сразу же после запуска. Не важно, будет ли это строчка "Выход" в меню, квадратик с крестиком в правом верхнем углу или просто сообщение о том, какую кнопь надо нажать и когда. Главное, чтобы сразу же было ясно, как выйти из программы.

Далее, ввод коэффициентов. В моем примере ввод реализован крайне ... отвратительно, короче. Чтобы ввести коэффициенты, надо вводить их в одну строку через запятую, иначе будет появляться унылое сообщение о необходимости повторного ввода (в Power Basic таких сообщений не будет!). Как пользователь может догадаться о столь диких правилах вода, я не представляю.

Поэтому я вам очень не советую применять данный метод ввода "ВСЕ переменные В ОДНОМ операторе INPUT", если, конечно, ввод через запятую не будет интуитивно подразумеваться. Единственный пример, который я могу сейчас придумать, это ввод дробных чисел (десятичных дробей), когда дробная часть от целой отделяется запятой. В этом случае оператор INPUT a,b запомнит в "а" целую часть, а в "b" - дробную. Но если пользователь введет вместо дроби целое число, то опять возникнет ошибка ввода.
Короче ... ну, ясно.

Лучше каждую величину вводить своим оператором INPUT, выдавая при этом соответствующие текстовые запросы и пояснения о порядке ввода.

Далее, на основе введенных коэффициентов, вычисляется дискриминант d. Здесь уже ошибка, а не недочет. Может, ведь, быть такая ситуация, когда дискриминант вычислять не нужно. Не можете представить такой ситуации? И это несмотря на то, что я об этом уже не раз говорил?

А ежели юзер введет коэффициент "а" равным нулю? В этом случае у нас уже будет совсем не квадратное уравнение, а самое, что ни на есть, линейное. И дискриминант здесь будет совсем даже лишним. Поэтому нам нужно проверять, что же вводит юзер в качестве "а". Это тем более важно, потому что у нас далее есть формулы, где "а" стоит в знаменателе. А на ноль, как известно, делить нельзя. Если все-таки сделать это, то возникнет ошибка и программа аварийно завершится. Нам, как разработчикам этого совсем не хочется, не так ли?

Т.е., нам нужно добавить в программу еще ветвь, которая бы обрабатывала ситуацию, когда a=0 и решала бы линейное уравнение.

Могут еще возникнуть ситуации, когда b=0 или c=0. Как вы помните, в математике есть специальные методы для решения таких уравнений, более простые, чем через дискриминант. Но нам добавлять их в программу нет никакой радости, так как это ее только усложнит. А вот ситуации, когда одновременно и "а" и "b" или "c" равны нулю предусмотреть стоит.

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

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

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

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

CLS 
PRINT "Программа для решения квадратных уравнений (ax^2+bx+c=0)" 
PRINT "Для продолжения нажмите ПРОБЕЛ."; 
DO 
  PRINT "Нажмите ESC для выхода." 
  DO 
    ch$=INKEY$ 
  LOOP WHILE ch$="" 
  IF ch$=CHR$(27) THEN END 
  CLS 
  PRINT "a*x^2+b*x+c=0" 
  PRINT 
  PRINT "Введите коэффициенты уравнения:" 
  INPUT "a=",a : INPUT "b=",b : INPUT "c=",c 
  PRINT "Решаем уравнение ";a;"*x^2+";b;"*x+";c;"=0 :" 
  IF a=0 THEN 
    IF b=0 AND c=0 THEN 
      PRINT "X - любое число." 
    ELSEIF b=0 AND c<>0 THEN 
      PRINT "Уравнение не имеет корней." 
    ELSE 
      LET x=-c/b 
      PRINT "X=";x 
    END IF 
  ELSE 
    LET d=b^2-4*a*c 
    SELECT CASE d 
      CASE IS<0 
        PRINT "Уравнение ";a;"*x^2+";b;"*x+";c;" не имеет действительных корней." 
      CASE 0 
        LET x=-b/2/a 
        PRINT "Уравнение имеет два одинаковых корня:" 
        PRINT "X1,2=";x 
      CASE IS>0 
        LET sd=SQR(d) 
        LET x1=(-b+sd)/2/a : LET x2=(-b-sd)/2/a 
        PRINT "Уравнение имеет два различных корня:" 
        PRINT "X1=";x1;CHR$(13);"X2=";x2 
    END SELECT 
  END IF 
  PRINT : PRINT 
  PRINT "Для того, чтобы решить еще одно уравнение, нажмите ПРОБЕЛ.";
LOOP UNTIL 0 

Программа стала существенно сложнее. Но вы скомпилируйте оба варианта и посмотрите, какой вам больше понравится. У меня первый вариант занял 3,5 КБ на диске, второй - 5 КБ. Полагаю увеличение размера исполняемого файла в данной ситуации более чем оправдано.

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

Как видите, за исключением "приветствия", програма представляет из себя тело бесконечного цикла DO ... LOOP UNTIL 0. Бесконечным он является, потому что выполняется ПОКА 0 не равен истине. А ноль всегда ложь, как вы знаете, и ничто не в силах это изменить.

Другим приемом является использование конструкции множественного выбора SELECT CASE для изучения значения переменной d. Применяя SELECT CASE я, тем самым, освобождаю себя от необходимости использовать GOTO (этому же способствуют и циклы DO...LOOP).

Отказываюсь от GOTO я, конечно же, не по идеологическим причинам, а из соображений удобочитаемости и надежности программы. Те, кто уже писал программу для решения задачи basic/1/5, могут сравнить свой вариант с моим. И если у вас не завышено самомнение, то вы увидите, что читать мою программу проще (вы должны, конечно, уметь читать программы:). Нет необходимости перескакивать из начала текста в конец, отслеживая куда же это ведет GOTO.

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

 ...
IF D<0 THEN GOTO metka
 ...
LET D=A+C/S
GOTO metka
 ...
 ...
metka:
LET G=SQR(D)
PRINT G
 ...

Совершенно абстрактный пример. Представьте, что программа очень большая и указанные строки сильно разнесены в ее тексте. Испытывая программу, вы обнаруживаете, что иногда в строке LET G=SQR(D) возникает ошибка из-за невозможности извлечь корень из отрицательного числа. Вы ставите перед этой строкой условие:

IF D<0 THEN PRINT "Ошибка":END

При этом вы забываете про строчку IF D<0 THEN GOTO metka (потому что она находится черт знает где, и вы ее писали три недели назад). Все работает. Ок. Но вдруг через год коммерческой экспуатации программы в одном уважаемом банке возникла ситуация, при которой программа завершает свою работу по непонятным причинам, когда оператор пытается работать со счетом клиента. Просто компьютер в силу каких-то обстоятельств натыкается на ту злополучную строчку. Найти причину такого поведения программы, имеющей текст из доброй сотни тысяч строк кода, будет очень нелегко.

Но это, как я сказал, абстрактный пример. С реальными примерами вы столкнетесь в ходе работы над своими программами.

Но вернемся к нашей программе. Она имеет ряд интереснных (для вас) моментов, поэтому я предлагаю ее более или менее подробно рассмотреть, так сказать построчно.

Кстати, если Вы подписаны на HTML версию рассылки, а Вы
...(определение типа подписки)...
как раз, на нее и подписаны, то Вы можете поводить курсором мыши над текстом программы и посмотреть всплывающие подсказки относительно той или иной строчки кода.

{Сообщение для Константина Даниленко: Константин, это именно тот способ комментирования исходников, о котором я Вам говорил.}

CLS - это оператор очистки экрана (CLear Screen). Очищать экран иногда бывает полезно.

Строка вторая. Печатается название программы. Довольно-таки неплохо сразу дать пользователю информацию о том, что за программу он запустил.

Затем печатается предложение пользователю нажать некие кнопки для продолжения работы программы либо для выхода (может он запустил программу по ошибке).

Заметьте, что на уровне исходного текста это сообщение разделено на две части: первое предложение (третья строка программы) находится за пределами цикла, второе предложение (про кнопку ESC) уже в цикле. Напротив, на экране пользователь видит эти два предложения на одной строке. Второе предложение подписывается при запуске программы к первому, а после очередной итерации внешнего цикла (у нас два цикла - внешний и внутренный, организующий паузу) оно подписывается к предложению решить еще одно уравнение (для этого на конце у соответствующих операторов PRINT стоит ";"(точка с запятой). Конечно, вам не обязательно поступать таким хитрым образом, просто я хотел показать, что так, в принципе можно, делать.

Основная часть программы является телом бесконечного цикла. Можно было бы обойтись без цикла, используя IF, GOTO и метку (в этом случае тоже был бы цикл, но здесь я под циклом понимаю конструкцию типа DO...LOOP), но с помощью цикла мы делаем программу функционально и структурно более законченной и, к тому же более красивой.

Я понимаю, что о хорошем стиле программирования говорить сейчас не уместно, во-первых, для начла надо иметь хотя бы какой-нибудь стиль, во-вторых, не думаю, что мой стиль может считаться 100% хорошим, да и вообще... Но все-таки, более красиво написанная программа, с более ясной и четкой структурой, работает более стабильно. Объясняется это не какой-то магией, а тем, что в хорошо написанной программе легче найти и исправить ошибки, которые обязательно бывают в любой программе.

Кого-то может удивить, что я вас сначала познакомил с оператором GOTO, а теперь говорю, что использовать его не надо. На самом деле я не противник GOTO (или как сейчас говорят goto-ненавистник), я четко представляю, когда его можно и нужно использовать, а когда лучше обойтись другими средствами, чему и вас пытаюсь научить. А вот если бы я не рассказал про GOTO, было бы, действительно, удивительно говорить о том, что использовать его не рекомендуется.

К этому вопросу мы еще будем возвращаться, а пока продолжим анализировать программу.

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

Обратите внимание, как опрашивается клавиатура: функция INKEY$ вызывается только один раз и возвращаемое ей значение запоминается в строковой переменной, которая затем и анализируется. Не нужно в подобных случаях вызывать INKEY$ несколько раз! INKEY$ это не переменная, это функция! Вызвать функцию INKEY$ все равно, что посмотреть на часы - каждый раз возможно различное значение.

IF ch$=CHR$(27) THEN END

Если пользователь нажал ESC, то программа завершается. Мной, при проверке ДЗ, было замечено, вы любите в подобных ситуациях делать в конце программы метку и гонять туда компьютер с помощью goto:

IF ch$=CHR$(27) THEN GOTO non
 ....
non:END

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

Запомните, программа может завершиться в трех основных случаях: при достижении ее конца (последней строки текста); при встрече компьютером оператора END (или STOP); в случае возникновения ошибки периода иполнения (runtime error).

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

Кстати о версиях Бейсика. Второй вариант программы не будет работать в Power Basic (похоже, там не поддерживается ключевое слово IS в конструкциях типа CASE IS<0).

Продолжаем. После паузы идет ряд операторов PRINT и INPUT. Комментарии здесь излишни.

Сразу после ввода коэффициентов уравнения начинается их анализ и, собственно, решение уравнения. Прикинув, какие ситуации могут возникнуть, я разделил ход выполнения на две ветви: ветвь линейных уравнений (а=0) и ветвь квадратных уравнений (а<>0) и оформил это с помощью конструкции IF.

Линейное уравнение b*x+c=0 имеет два коэффициента, каждый из которых может быть равен или не равен нулю. Итого имеем четыре различные ситуации (два параметра, два состояния (равен нулю/не равен нулю) - 2^2=4). Смысл не в простом равенстве или не равенстве нулю коэффициентов. Дело в том, что это равенство или и не равенство сильно влияет на решение уравнения. А такие решения, как "X - любое число" и "X - пустое множество" вообще нельзя записать на Бейсике, поэтому ситуации, когда такие решения могут иметь место, надо отлавливать на ранней стадии. Это мы, как раз, и делаем, проверяя значения коэффициентов b и c на предмет равенства их нулю.

Четыре ситуации, о которых я сказал, это:

1)b=0;c=0 - X - любое число
2)b=0;c<>0 - корнет нет
3)b<>0;c=0 - X=0
4)b<>0;c<>0 - X=-c/b

Напрашивается решение обработать эти ситуации по следующей схеме:

                     |
             да     b=0        нет
            -----------------------
            |                     |
       да  c=0  нет          да  c=0  нет
      --------------        --------------
      |            |        |            |
   x-любое   корней нет    x=0        x=-c/b

Имеем четыре ветви и два уровня вложенности операторов IF. Но я пошел по другому пути. Обратите внимание на варианы 3) и 4). Не кажется ли вам, что их можно объединить, ведь при с=0 x=-c/b=0. После объединения вариантов 3) и 4) мы будем уже иметь 3 ветви. Теперь уменьшим уровень вложенности операторов IF: объединим проверку коэффициентов b и c на равенство нулю в одно сложно условие, используя логическую операцию AND. Результат этих манипуляций вы можете видеть в исходном тексте программы.

В ветви квадратных уравнений выполнение программы тоже разделяется, в зависимости от переменной d, которая может иметь три различных и интересных нам состояния: <0, =0, >0. Имеем одну переменную и более чем два состояния. Значит для организации ветвления удобно применить SELECT CASE, что мы и делаем.

Вот, в общем-то и все остальные "хитрости" посмотрите в комментариях к программе (поводите курсором мыши над текстом).

ЗАКЛЮЧЕНИЕ

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

Итак, что вас ждет в рубрике ...

Теория

- системы счисления
- простые типы данных и их представление в памяти компьютера
- сложные типы данных

Бейсик

- практическое применение различных видов циклов
- операции над символьными данными (строками)
- подпрограммы
- массивы
- файлы

HTML

- использование графики
- итоговое занятие (повторение и систематизация изученного материала)
- таблицы
- фреймы

DOS

- кодирование символов
- обзор команд DOS
- практическое применение команд DOS - bat-файлы

Конечно же, мои планы относительно этого могут и измениться, даже сегодня вечером.

Ну ладно... Пока. До 15-го. В смысле, выпуска :).
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

С уважением,
Вячеслав Stac Мацнев mailto:stac@stacmv.net
04.03.01.



http://subscribe.ru/
E-mail: ask@subscribe.ru
Поиск

В избранное