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

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


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

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

Выпуск 22

1 SEP 2001

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

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

Если все в сборе, то, пожалуй, начнем.

ОТСЕБЯТИНА .::. Деление на ноль

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

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

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

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

Абсолютное большинство программ для решения квадратных уравнений, написанных вами в ответ на задачу basic/1/5, подвержены ошибке "Деление на ноль".

Проверить это легко, очень легко. И я не пойму, почему я до сих пор не научил вас делать это.

Запустите свою программу и введите значение коэффициента A равное нулю. Ошибка возникнет при расчете корней, т.к. он там стоит в знаменателе.

Вот формула: x=(-b+sqr(d))/(2*a)

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

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

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

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

Это цитата из книги Б. Кернигана и Р. Пайка "Практика программирования":

          В ноябре 1998 года в журнале Scientific American
          был   описан  инцидент,  произошедший  на  борту
          американского  ракетного крейсера Yorktown. Член
          команды по ошибке вместо значимого числа ввел 0,
          что  привело  к  ошибке  деления на ноль; ошибка
          разрослась и  в  конце  концов силовая установка
          корабля оказалась  выведена  из строя. Несколько
          часов Yorktown  дрейфовал  по  воле волн - а все
          из-за того, что в программе не была осуществлена
          проверка диапазона вводимых значений.

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

ТЕОРИЯ .::. Сложные типы данных. Массивы.

Сейчас в мире существуют огромные массивы информации. Существуют также и лесные массивы. О том, что понимают под массивами программисты читайте в этой рубрике.

Информация бывает разных типов. Мы уже договорились, что две большие группы типов это числа и строки (или символы, нет все же строки). INTEGER, LONG, SINGLE, DOUBLE, STRING это все ключевые слова Бейсика обозначающие соответствующие простые типы данных.

Простыми они считаются по следующей причине. В переменной простого типа можно хранить только один элемент данных - одно число или одну строку.

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

Банальный пример, нужно проанализировать изменение температуры воздуха за неделю. В неделе семь дней, значит нужно семь переменных для хранения соответствующих значений температуры.

Это реально. Но если нужно проанализировать температуру за месяц (30 дней) или за год (365 дней). Или за 10 лет (3652 дня). Создать 3000 переменных, конечно, можно.

temp1=-20
temp2=-20
temp3=-19
 ...
temp3651=-17
temp3652=-19

Можно их все распечатать.

PRINT "День 1:";temp1
PRINT "День 2:";temp2
PRINT "День 3:";temp3
 ...
PRINT "День 3651:";temp3651
PRINT "День 3652:";temp3652

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

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

Массив - набор данных одного типа, имеющих одно общее имя.

Элемент массива - единица данных, составляющих массив.

Индекс элемента - уникальный идентификатор элемента массива.

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

Впрочем, чего это я, на самом деле все так и есть!

Как и любую переменную, массив нужно объявлять, причем явно.

DIM temp(1 TO 3652) AS INTEGER

Эта команда резервирует память под массив из 3652 элементов типа INTEGER. Т.е. она выделяет память для элементов с индексами от 1 до 3652. Тип элементов здесь важен, так как он определяет размер отводимой памяти.

Теперь можно обращаться к любому элементу по его индексу:

temp(1)=-20
temp(2)=-20
temp(3)=-19
 ...
temp(3651)=-17
temp(3652)=-19

........... ...........

PRINT "День 1:";temp(1) PRINT "День 2:";temp(2) PRINT "День 3:";temp(3) ... PRINT "День 3651:";temp(3651) PRINT "День 3652:";temp(3652)

Круто, но пользы от массива пока не заметно.

Теперь пришло время применить цикл. Внимание! Печатаем все 3652 значения температуры!

FOR i=1 TO 3652
  PRINT "День ";i;": ";temp(i)
NEXT i

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

Найти среднюю температуру?

average=0
sum=0
FOR i=1 TO 3652
  sum=sum+temp(i)
NEXT i
average=sum/3652

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

Пускай нам надо знать как менялась средняя температура по годам:

REM Программа 22.1
REM При объявлении массива можно опускать нижний индекс, при этом
REM подразумевается, что он равен нулю.
DIM average(10) AS INTEGER 'Массив средних температур.
DIM temp(3652) AS INTEGER

start = 1 'Задаем начало года FOR year = 1 TO 10 'Цикл по годам days = 365 'В году 365 дней IF year = 4 OR year = 8 THEN days = 366 'Пусть 4 и 8 годы будут високосными finish = start + days - 1 'Задаем конец текущего года sum = 0 FOR day = start TO finish 'Цикл по дням текущего года sum = sum + temp(day) NEXT day average(year) = sum / days start = finish + 1 'Задаем начало следующего года NEXT year CLS FOR i = 1 TO 10 PRINT "Год: "; i, "Средняя температура: "; average(i) NEXT i

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

Сегодня в других рубриках мы еще поработаем с массивами в контексте данного примера с температурами. Теории же на сегодня хватит.

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

Хочу дать совет преподавателям: коллеги, никогда не рассказывайте ученикам все что знаете о массивах на одном занятии! Разбейте материал минимум на два занятия.

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

Откройте глаза, расставьте уши пошире. От теории переходим к практике.

БЕЙСИК .::. Работа с файлами - 2. Формат CSV.

При работе с массивами часто необходимо сохранять их в файлах или, наоборот, считывать из файлов.

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

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

Эти данные, скорее всего, будут выложены в Интернете в формате HTML для просмотра посетителями сайта. Также вполне вероятно, что эти же данные будут доступны на сайте в формате какой-либо программы для работы с электронными таблицами.

Небольшие фирмы могут выкладывать свои прайс-листы в формате Excel 97. Но такая серьезная организация как метеослужба врядли станет так поступать.

Всем (возможно кроме наших небольших фирм) известно, что Excel это не единственное средство для работы с электронными таблицами.

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

Поэтому нужно выложить данные в таком формате, который был бы понятен абсолютному большинству программ-клиентов. Это формат CSV (Comma Separated Values - Значения разделенные запятой). Очень простой по сути формат, хотя при работе с ним и могут возникать трудности.

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

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

Таблица

*---------------------------------------------------------------*
*                    Средняя температура                        *
*---------------------------------------------------------------*
* Год         *1991*1992*1993*1994*1995*1996*1997*1998*1999*2000*
*-------------*----*----*----*----*----*----*----*----*----*----*
* Температура *14.9*15.1*15.3*14.8*15.2*15.4*15.1*15.6*15.4*15.0*
*---------------------------------------------------------------*

в CSV формате будет выглядеть так:

Средняя температура
Год,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000
Температура,14.9,15.1,15.3,14.8,15.2,15.4,15.1,15.6,15.4,15.0

Создайте текстовый файл с этим содержимым и назовите его csvtest.csv.

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

Но мы на это пока отвлекаться не будем. Договоримся между собой использовать пока запятые только в качестве разделителя.

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

INPUT #1,a,b$ считает из файла #1 два значения разделенных запятой или символами CR и LF, и присвоит их переменным a и b$. Обратите внимание на то, что первая переменная числовая, вторая - строковая. В читаемом файле значения должны идти в соответствующем порядке. Т.е. сначала должно идти число (запись числа! т.к. файл текстовый, то в нем есть только строки), а затем строка.

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

Давайте прочитаем файл csvtest.csv. Глядите, это код:

DIM year(10)                       'В таблице данные за 10 лет.
DIM temp(10)
OPEN "csvtest.csv" FOR INPUT AS #1
INPUT #1, header$                  'Читаем первую строку - заголовок.
INPUT #1, yh$                      'Первое значение во второй строке -
                                   'слово "Год".
FOR i = 1 TO 10                    'Заполняем массив номерами годов.
  INPUT #1, year(i)
NEXT i
INPUT #1, th$                      'Первое значение во второй строке -
                                   'слово "Температура".
FOR i = 1 TO 10
  INPUT #1, temp(i)                'Заполняем массив значениями
NEXT i                             'температуры.
CLOSE #1
CLS
PRINT header$                      'Печатаем таблицу.
PRINT
PRINT yh$, th$
FOR i = 1 TO 10
  PRINT year(i), temp(i)
NEXT i

В результате работы программы на экране вы увидите:

Средняя температура

Год Температура 1991 14.9 1992 15.1 1993 15.3 1994 14.8 1995 15.2 1996 15.4 1997 15.1 1998 15.6 1999 15.4 2000 15

В работе этой программы важен один момент - файл csvtest.csv должен содержать именно такие данные, которые мы подразумеваем, т.е:

строка, строка, число, число, число, число, число, число, число,
число, число, число, строка, число, число, число, число, число, число,
число, число, число, число.

Они могут разделяться либо запятой либо последовательностью CR/LF, но нельзя использовать два разделителя одновременно.

Объясняю почему.

1,2,3,,4 - в этой строке 5(!) значений.

Здесь тоже 5 значений:

1
2
3

4

Это все равно что пустая ячейка в таблице.

Ок. Проехали. Теперь обсудим запись в CSV файл.

Т.к. CSV это текстовый файл, то здесь будут работать все приемы, рассмотренные нами в прошлом выпуске.

А если конкретнее, то для записи можно использовать PRINT, самостоятельно заботясь о расстановке запятых.

Пример:

PRINT "Имя,Вася,Катя,Дима,Ольга"
PRINT "Телефон,123,056,450,102"

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

PRINT "Телефон,",phone(1),",",phone(2),",",phone(3),",",phone(4)

Но не переживайте, есть такой оператор, называется WRITE. Смотрите, что написано о нем в хелпе QBasic'а.

WRITE [[#]номер_файла%,] выражение

номер_файла% Номер открытого последовательного файла. Если номер файла опущен, WRITE выводит данные на экран. выражение Одна или несколько переменных или выражений, разделенных запятыми, значения которых записываются на экран или в файл.

WRITE вставляет запятые между объектами и заключает в кавычки строки в той последовательности, в которой они написаны. WRITE записывает значения в файл в той форме, которая может быть считана оператором INPUT.

Как видите, Бейсик, не такой уж плохой язык :-).

Я не буду показывать пример использования WRITE, т.к. ничего сложного для вас в его использовании нет. Попробуйте сами написать программу, которая бы писала и читала CSV файлы. А помогут вам в этом несколько задач из третьего ДЗ :).

P.S. Для тех кто забыл или не знает, что такое CR/LF:

Это служебные символы с кодами CR: 13 (0Dh); LF: 10 (0Ah). В текстовом файле, каждая строка завершается символами CR и LF, именно в таком порядке.

Это относится к платформе DOS. В Unix для завершения строки обычно используется только LF, а в MacOS только CR.

Эта разница между платформами играет важную роль при обмене между ними текстовыми данными.

При записи в файл PRINT, например, в конце строки записывает CR/LF, если оператор не завершен точкой с запятой.

Откройте любой текстовый файл в hex-редакторе и поищите там символы CR/LF. Это вам пригодится в дальнейшем.

СТРАТЕГИЯ И ТАКТИКА .::. Генерация массива

Сегодня мы начинаем разговор о тестировании программ. Лирическое введение о том, зачем это нужно оставлю на другой раз. А сейчас обсудим один простой и очень конкретный вопрос.

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

Опубликованный выше вариант ничего толком не напечатает, потому что массив температур temp не содержит никаких данных. Их нужно как-то ввести.

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

Ввод из файла не всегда подходит, особенно, если, как в нашем случае, файла-то нету.

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

Сделать это несложно с помощью цикла. Вопрос в том, какие значения использовать.

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

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

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

Я, например, для последнего случая использовал следующий цикл:

REM К программе 22.1
REM Вставьте этот код после последнего DIM в программу 22.1
FOR i = 1 TO 3652
  temp(i) = (RND(1) * 70) - 20
NEXT i

RND() возвращает случайное число от 0 до 1 (при любом аргументе). Умножая его на 70, получаем уже число от 0 до 70. Отнимая 20, получаем более или менее реальные значения температуры: от -20 до 60. Насчет верхнего предела возникают, конечно, некоторые сомнения. Но, вот странно, "компьютерный кубик" выкидывает чаще числа близкие к нулю, чем к единице, что нам на руку.

Запустив программу, я получил рад температур, крутящихся возле +15 градусов, что отражает реальное положение дел у нас на планете и, несомненно, греет душу. Да и вообще все греется, примерно на +1 градус каждые 100 лет.

Ну, теплого нам всем следующего лета и морозной [этой] зимы!

ДОМАШНЕЕ ЗАДАНИЕ .::. basic/3

Третье зачетное ДЗ по Бейсику. Код:basic/3

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

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

Поэтому, особенно в этот момент, желаю успеха, друзья!

Задача 1

Напишите программу, которая реализует эффект заполнения заданной прямоугольной области экрана точками (".") от центра к краям.

Задача 2

Напишите следующие функции:

BinToDec$(), HexToDec$(), OctToDec$(),
DecToBin$(), DecToDec$(), DecToDec$()

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

Например, BinToDec$("111") должна возвратить "8", а OctToDec$("8") - "10".

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

Задача 3

Напишите программу для проверки работы функций из задачи basic/3/2.

1) Программа должна читать входной CSV файл, каждая строка которого содержит три значения:

- число в какой-либо системе счисления с указанием этой системы посредством суффикса (как принято в рамках рассылки). Пример: "134o";
- обозначение целевой системы счисления, в которую нужно перевести число, в виде трехбуквенного идентификатора (bin,oct,dec,hex) в любом регистре. Пример: "DEC";
- правильный ответ - запись числа в целевой системе счисления без указания этого числа. Пример: "92".

Пример строки входного файла:

10h,oct,20

2) Определить в какую систему нужно перевести число, вызвать соответствующую функцию.

3)Сравнить полученный ответ с правильным (из файла).

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

5) Сохранить отчет в файле.

Задача 4

Напишите программу, реализующую эффект "бегущие муравьи".

Необходимо ввести с клавиатуры строку и отобразить ее на экране в "бегущей" рамке.

        Кадр 1                    Кадр 2
       - - - - - -               - - - - - -
       |  СТРОКА  |             |  СТРОКА  |
        - - - - - -             - - - - - -

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

Для рамки используйте символы псевдографики.

Задача 5

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

Запись в файл реализовывать не нужно.

Задача 6

Модернизируйте программу 21.2 таким образом, чтобы она вела себя аналогично команде DOS COPY, т.е. воспринимала тот же формат командной строки для копирования и конкатенации файлов.

Организуйте поддержку всех ключей, которые воспринимает команда COPY.

Задача 7

Доработайте программу 9.3. Исправьте все недочеты и ошибки.

Добавьте файловые функции: чтение CSV файла, сохранение в CSV файл.

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

Разрешается полностью изменить интерфейс, главное чтобы выполнялись все заявленные функции.

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

Задача 8

Контрольная работа.

ОКРУЖЕНИЕ .::. Город информации

" Visual C++ для начинающих "
" Delphi 6: первое знакомство "
" Основные ошибки начинающих дизайнеров "
" Любимые игрушки хакера "
" Полезные советы VB "  

Эти материалы и много-много других вы найдете в компьютерной библиотеке InfoCity.

Рекомендуем!

Рассылка "Новости электронной компьютерной библиотеки InfoCity"

InfoCity - виртуальный город компьютерной документации.
Богатая коллекция компьютерной литературы любого направления. Библиотека будет полезна и начинающему пользователю, и специалисту.
 -  Более 10.000 электронных документов.
 -  Грамотная и удобная организация разделов.
 -  Каждую неделю более 30 новых книг и статей.

Обучайтесь с нами!
http://www.infocity.kiev.ua
 

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

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



http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу
Рейтингуется SpyLog

В избранное