[ Обновленные темы · Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 2
  • 1
  • 2
  • »
Модератор форума: ППШ, Chift  
паскаль

semen
Дата: Суббота, 30.10.2010, 17:28 | Сообщение #1
Администраторы
Сообщений: 301
Репутация: 150
Теория

Итак вы не любите комндную строку, файлы без иконки для вас не существуют. Чёрный экран - символ выключенного компьютера. Синий - символ краха системы. Добро пожаловать в XX век! Забудьте всё что вы знали - это вам не понадобится. Хотя всё таки некоторые советы по настройке винды я могу дать (не знаю как они будут работать в XP но 95/98/Me - спокойно!)
Итак для начала представим, что вы установили Pascal в директорию C:\BP. Представили ? Значит с воображением у вас не так уж и плохо! Хороший знак! :))
В меню эксплорера выбираем: Вид -> Свойства Папки. Там открываем закладку: Типы Файлов и нажимаем кнопку Новый тип. Сразу же давим кнопку: Изменить значёк. И выбираем который нравится. Уфф! Полдела уже сделанно!

В поле описание типа вводите что - то такое: исходник на паскале
В поле расширение пишите: pas
Нажимаете кнопку создать. В поле действие: &Открыть или &Open, кому как нравится.
В поле Приложение, исполняющее действие: C:\BP\BIN\BP.EXE, помните мы представляли, что установили Паскаль в C:\BP ???? Нажимаете на следующие кнопки: Назначить по умолчанию (не забудьте выделить при этом действие Open), снимаете все галки кроме: Подтверждать, нужно ли открывать после загрузки, нажимаете кнопочку: Закрыть. Затем перезагружаете компьютер и найдите файл с расширением pas - опа на нём висит иконка! Доубле клик - и вы в среде Borland Pascal и файл открыт тот самый, который и нужно!!! УРА!!!!!

Второй пункт настройки - файл autoexec.bat (помните дядя в магазине советовал ничего в него не писать:) Итак вперёд: Пуск->Выполнить набираете sysedit - загрузится программа редактирования системных файлов - самый верхний из них и есть тот самый autoexec.bat
Найдите там строчку которая начинается словами SET PATH (например у меня она примерно такая:
SET PATH=C:\BC5\BIN;%PATH%;C:\USR\BIN
допишите в конец C:\BP\BIN (т.е. она превратится в такую
SET PATH=C:\BC5\BIN;%PATH%;C:\USR\BIN;C:\BP\BIN
Если у вас всё это вышло и компьютер загружается - значит вы всё сделали правильно smile

Теперь кое-какие сведения о самой среде Паскаля. Она вообщем-то похожа текстовый редактор. Думаю что все знают как пользоваться хотя бы простым блокнотом. И поэтому тут ничего объяснять не надо. Так что в заключение я хотел бы привести список некоторых горячих клавиш, которые ещё помню. Остальные клавиши узнаете в процессе обучения:
Назначение Клавиша Аналог в меню
Доступ к меню Alt+Первая Буква Названия меню или Alt+F10
Открыть файл F3 File->Open
Сохранить файл F2 File->Save
Выход Alt+X File->Exit
Отменить последний ввод Alt+BackSpace Edit->Undo
Выделить текст Shift+стрелки
Отменить выделение Shift+стрелки
Удалить выделенное, поместив его в буффер обмена Shift+Del Edit->Cut
Копировать Ctrl+Insert Edit->Copy
Вставить Shift+Insert Edit->Paste
Удалить выделенное Ctrl+Del Edit->Clear
Переключение между окнами Alt+ номер окна (циферка в правом углу)
Закрыть окно Alt+F3 Window->Close
Развернуть во весь экран / свернуть F5 Window->Zoom
Следующее окно F6 Window->Next
Предыдущее окно Shift+F6 Window->Pervious
Размеры окна / передвинуть Ctrl+F5 Window->Size / Move
Список открытых окон Alt+B Window->List
Help Index Shif+F1 Help->Index

И ещё пару слов:
Кодировка ДОС и Windows не совпадают!!! Т.е. если вы перенесёте текст программы (который содержит русские буквы) в редактор Паскаля, то вместо руских букв увидите какую-то кракозябу. Однако это не касается английских букв - они одинаковы.
Буффер обмена
Вот чуть не забыл! У Window'sa и у Паскаля различные буферы (или буфера smile обмена. Т.е. если вы захотите копировать текст из редактора BP, то вам надо произвести следующие манипуляции: нажать Alt+Enter (теперь BP у нас в оконном режиме). Ставим курсор на строчку куда нужно вставить и нажимает на иконку чУмодана вверху! А вот, что бы копировать текст из редактора в буфер Window'sa нужно выделить кусок текста мышой, предварительно нажав кнопку с квадратиком, она рядом с чумоданом. Однако очень большая трудность: нельзя будет передвигаться с помощью скролл баров, т.е. большой текст придётся переносить в несколько приёмов!
Послесловие

Ну вот вроде бы и всё! Старт дан! Марафон начался! Успехов вам в изучении программирования.


 

semen
Дата: Суббота, 30.10.2010, 17:29 | Сообщение #2
Администраторы
Сообщений: 301
Репутация: 150

Теория

Для начала разоберёмся с шестнадцатеричной системой счисления. Ничего сложного в ней нет. Мы используем в жизни десятичную систему. Поэтому объяснения начну с аналогий: В десятеричной системе мы используем десять цифр - от 0 до 9 => в шестнадцатеричной цифр должно быть 16. С первой десяткой вроде ясно (те же арабские цифры от 0..9), а как быть с остальными?? Этот вопрос решили просто - взяли первые шесь букв латинского алфавита. Таким образом получается такой набор цифр:

0 1 2 3 4 5 6 7 8 9 A B C D E F

В дальнейшем, для предотвращения путаницы шестнадцатеричные числа я буду писать, ставя на конце букву h или H (например 12A4Dh ) H - это сокращение от англ. hexadecimal (шестнадцатеричное). Для краткости его иногда называют просто Hex. Вообщем-то h пришло из языка Ассемблера. Важно сразу же понять, что десятичные и шестнадцатеричные числа равны только в промежутке от 0..9, т.е. 10 = Аh, 11 = Bh .... 15 = Fh, а 11 не равно 11h.

Преобразование из шеснадцатеричной (HEX) в десятичную (DEC).

Для первода числа в Dec необходимо последовательно начиная с самой левой цифры умножать на 16 и складывать со следующей цифрой. Так как операции выполняются в 10-ом формате, то необходимо цифры A..F преобразовать в 11..15 smile Пример преобразования числа A7B8h:

Первая цифра А(10)
Умножаем на 16

Прибавляем следующую цифру, 7

Умноить на 16

Прибавить следующую цифру, В(11)

Умножить на 16

Прибавить следующую цифру, 8
Десятичное значение 10

Есть ещё один способ перевода из Hex в Dec: A7B8h = 10 * 16 3 + 7 * 16 2 + 11 * 16 1 + 8 * 16 0 = 42936 Этот способ по-моему несколько проще: берём самую правую цифру и умножаем на 16 в 0 степени, сдвигаемся на одну цифру влево и умножаем на 16 в 1, ......... и т.д. пока не дойдём до конца. Вспомните из математики: любое число в 0 степени равно 1 (кроме 0 есстественно)! Вся сложность этого метода состоит в том, что нужно помнить много степеней 16, на первый взгляд это тяжело, но по-переводив пару сотен чисел smile степени запомнятся сами собой. Теперь поговорим об обратном преобразовании из Hex в Dec. Для преобразования необходимо сначала исходное число разделить на 16; число получившеесе в остатке является младшей шестнадатеричной цифрой. Затем уже частное поделить на 16 и полученый остаток даст следующую влево 16-ую цифру. Продолжая таким образом пока в частном не получится 0, из остатков получим все необходимые 16-ые цифры. Тут нужно незабывать делать обратные преобразования, т.е. 10 преобразовывать в А, 11 в B и т.д. Пример переведём число 42936 в Hex: Частное Остаток Шестнадцатиричное значение

42936 / 16 2683 8 8 (младшая цифра)
2683 / 16 167 11 B
167 / 16 10 7 7
10 / 16 0 10 A (cтаршая цифра)

Тем самым ответ: 42936 = A7B8h. Не забудьте, что результат пишется, как бы наоборот, т.е. первый остаток является последней цифрой искомого числа!

Дома не забудьте попрактиковаться - переводите числа на листочке из одной системы в другую, благо есть чем проверять!
А нафига она нужна - эта система?

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

Ответа на этот вопрос придётся подождать ещё немножко sad Он в общем-то не сложный, но требует ещё кое-каких дополнительных знаний, которые вы получите на следующем уроке.


 

semen
Дата: Суббота, 30.10.2010, 17:30 | Сообщение #3
Администраторы
Сообщений: 301
Репутация: 150
Теория

Итак системой счисления, а вернее её основанием, называется колчество знаков. Так для десятичной системы - 10 знаков (от 0 до 9), шестнадцатеричной - 16 (0..F), а следовательно у двоичной всего 2 знака - 0 или 1. Обозначаются двоичные числа так: 110101b. b - это сокращение от слова Binary (двоичный). Рассмотрим несколько двоичных чисел: 0000b 0

0001b 1
0010b 2
0011b 3
0100b 4

и т.д...

Теперь о переводе в десятичную систему: перевод осуществляестся по тому же принципу, что и в шестнадцатеричной, т.е: 101101b = 1 * 2 5 + 0 * 2 4 + 1 * 2 3 + 1 * 2 2 + 0 * 2 1 + 1 * 2 0 = 45 = 2Dh Для тех, кто не очень внимательно читал предыдущий выпуск, повторюсь: берём самую правую цифру и умножаем на 2 в 0 степени, сдвигаемся на одну цифру влево и умножаем на 2 в 1, ......... и т.д. пока не дойдём до конца. Вся сложность этого метода состоит в том, что нужно помнить много степеней 2 (или той системы из которой переводим), на первый взгляд это тяжело, но по-переводив пару сотен чисел степени запомнятся сами собой. Как можно заметить, этот метод годится для перевода чисел из любых систем счисления в 10-ую, только нужно домножать на основание системы.

Теперь о переводе наоборот: из 10-ой в 2-ую. Тут тоже действует уже, надеюсь, известный вам способ деления, т.е. число делим на 2; число получившеесе в остатке является младшей двоичной цифрой. Затем уже частное поделить на 2 и полученый остаток даст следующую влево 2-ую цифру. Продолжая таким образом пока в частном не получится 0, из остатков получим все необходимые 2-ые цифры. Частное Остаток Двоичная цифра

45 / 2 22 1 1 (младшая цифра)
22 / 2 11 0 0
11 / 2 5 1 1
5 / 2 2 1 1
2 / 2 1 0 0
1 / 2 0 1 1 (старшая цифра)

Тут также нет ничего сложного.

Теперь о переводе в шестнадцатеричную систему: тут алгоритм посложнее: начиная с правой цифры разбиваете число на соответствующие 16-ой системе. Ясно это будет из дальнейших примеров: 101101b разбиваем справа: 1b = 1h, 01b=1h, 101b=5h 1101b=Dh СТОП! Первая правая цифра уже есть! Рассмотрим оставшуюся часть: 10b = 2h, и следовательно наше число 101101b = 2Dh. Почему мы остановились после 1101b ? Просто потому, что 01101b=Dh, а 101101b > Fh, а нам нужно получить только одну цифру числа. Ещё один пример, показан он по другому: 10101011b = (1010)(1011)b = ABh Т.е. нужно запомнить первые 16 чисел 2-ой системы и тогда сразу будет видно, когда нужно остановиться. Число 01011b так же равно Bh, но при этом нуль слева нам совсем не нужен, т.к. он не играет никаой роли, т.е. мы можем дополнить число любым количеством нулей слева и поэтому этот на этот нуль мы не смотрим, а следущее число 101011b > Fh = 1111b и значит мы производим разбивку по 4-ой цифре, т.е. (1010)b и (1011)b. Это и даёт нам конечный результат.

Двойчная арифметика. Сейчас мы заново научимся складывать числа столбиком smile 1011101 +1111001 11010110 Правила всё те же, что и для десятичной системы, но нужно помнить, что 1b + 1b = 10b. Вычитание - это всё наоборот, 10b - 1b = 1b, а правила - знакомые со школы!

А теперь поговорим об отрицательных числах. Начнём с простого числа (-1). Как же можно представить его в двоичном виде ?? Ответ кроется в следуещем соображении: (-а) = 0 - а :))) Тут вроде бы всё ясно, НО какой глубокий смысл! Рассмотрим число (-1): (-1) = 0 - 1, а теперь в двоичном виде: 000000000 -000000001 111111111 Каково ??? Удивительно, но это логично: попробуйте к 111111111111b прибавить 000001b и получится 0 !!!! Это очень интересное свойство двоичных чисел.

Ну а перевод отрицательных двоичных в 10-ую систему происходит так: переворачиваем все цифры на противоположные, т.е. вместо 0 ставим 1 и наоборот. 11111011b -> 00000100b переводим полученное в Dec 00000100b = 4 добавляем 1 4 + 1 = 5 ставим знак минус 5 -> -5 Ладно, а теперь всё проверим..... Запускаем виндовский калькулятор, ставим галочку Bin, вводим 11111011, ставим галочку Dec, и ...... видим на экране 251 ??? Что-то тут не так! В душу вкладывается сомнение: а не гон ли всё это? Устроим ещё одну проверку: берём (-5), ставим галочку Bin, и видим число : 1111111111111111111111111111011 ? Похоже, но не то! (Кстати попробуйте перевести его обратно в Dec :)). Вся фишка заключается в том, что виндовский калькулятор: не умеет преобразовывать отрицательные Bin в Dec. Об числе 1111111111111111111111111111011b : всё дело втом, что когда мы вычитали из 000b 001b, то мы не учли одной очень нужной вещи: к числу можно добавить любое количество нулей слева!! Т.е. 0 у калькулятора выглядит так: 00000000000000000000000000000000b :)) Вот и весь прикол! В (-1) на самом деле бесконечное множество 1.

А теперь ещё одно маленькое соглашение: что бы не уподобляться виндовскому калькулятору и не путать отрицательные числа с положительными (в Bin разумеется), мы будем писать положительные числа, добавляя слева нуль (например: 1 = 01b, 2 = 010b и т.д.), а отрицательные без него (-1 = 1111b)! Тем самым мы предотвратим возможную путаницу!
А нафига они нужны - эти системы?

Да уж волнующая тема! Теперь появилась ещё одна система, а зачем оно ? Ок. Начну отдалённо. Наименьшей единицей хранения информации является бит (ну это, как секунда в СИ). Соответственно бит может принемать два состояния, которые условно называют правда и ложь. Для обозначения этого можно использовать 1 (правда) и 0 (ложь). Чуствуете ? Пахнет двоичной системой счисления! Из бит складываются байты. 1 байт = 8 битам. Т.е. что бы закодировать 1 байт нам нужно написать последовательность из 8 битов: 10110110 Сколько же может быть комбинаций еденичек и нулей? Оказывается всего 256. Этих 256 комбинаций хватает, что бы закодировать в них символы алфавита, поэтому с помощью 1 байта мы можем представить символ подчти каждого алфавита. Естественно, что мы таким образом кодируем номер символа, а выводом нужного знака на экран по этому номеру занимается видеокарта. Хорошо, отношение двоичной системы к битам вроде бы понятно, но шеснадцатеричная-то тут при чём??? Да вообщем-то она и не при делах smile Просто её удобнее использовать. В самом деле hex-число, состоящее из 2-х знаков полнотью описывает все 256 возможных комбинаций. FFh = 255 (256 комбинацию составляет число 00h). Получается, что 1 шеснадцатеричная цифра может закодировать пол-байта (это так называемый огрызок, или нибл). Согласитесь чем для каждого байта писать восемь цифр удобнее написать две, а после окажется, что рабтотать с ними удобнее, чем с десятичными.
Послесловие

Думаю, если вы вникли во всё выше сказанное, то смысл фразы будет вам понятен: в мире существует 10 категорий людей: те, которые понимают двоичную систему счисления и те, которые её не понимают :))

Ну вот теперь вы изучили мат. часть smile Это самое главное! Со следующего выпуска мы начнём писать программы на Паскале. Если вы ещё им не обзавелись, то самое время это сделать. На сайте в разделе Инструменты лежит пара ссылок, на работо способность они к сожалению не проверены. Лучшим же выходом является попрежнему покупка CD диска с Паскалем. Напомню, что нам нужен Borland Pascal 7 (или Turbo Pascal 7). На этом всё.


 

semen
Дата: Суббота, 30.10.2010, 17:31 | Сообщение #4
Администраторы
Сообщений: 301
Репутация: 150
Вступление

Что такое Hello World! - спросите любого програмиста - он тихо посмеётся про себя и посмотрит на вас, как на идиота. Програмкой типа Hello World! называют простую программу вывода текста на экран. Надеюсь, что будем выводить понятно smile Кстати Hello world! по русски значит "Привет, мир!". 90% начинают именно с программок типа Hello world!
Программа

Сразу начнём с текста программы:

Program HelloWord;

begin
writeLn ('Hello World!')
end.

Теория

Итак быстренько запускаем паскаль, вбиваем (в смысле набираем) эти 4 строчки, нажимаем F2 (сохранить), вводим имя, нажимаем Ctrl+F9 (исполнение)..... Какого чёрта! Ничего не вывелось! Досадно наверное ?? А ведь всё очень просто, как всегда smile

* Что нужно сделать, что бы увидеть результаты программы? Ну прежде всего написать программу. Этот важный пункт мы сделали. Можно загнуть палец на левой руке, правая нужна, что бы шуровать мышой smile
* Откомпилировать её. Что это такое позже, пока достаточно знать, что это происходит при нажатии Ctrl+F9.... загните второй палец ...
* Запустить её. Это тоже входит в нажатие Ctrl+F9.... Таким образом на руке должно быть загнуто три пальца
* Теперь снимите правую руку с мыши и попробуйте нажать Alt+F5... я знаю это тяжело, но от этого зависит результат.... Это был четвёртый палец...

УРА!!! Можете разогнуть пальцы и радостно подпрыгнуть!!!!! Hello World! - эту строчку вывела наша программа!!!

* Когда пройдет первая радость от содеяного, давайте рассмотрим теперь всё это дело более серьёзно (по строкам): Итак слово Program - служебное слово языка паскаль (вы заметитили я его выделил ? :)). Служебные слова - это такие слова, которые программист использует в строго определённых целях. После слова Program идёт имя программы, однако это имя может и не совпадать с именем файла. Это служебные данные и на программе они не отражаются. Как это можно проверить? КОНЕЧНО же экспериментом: удалите эту строчку, программа будет прекрастно работать !! Как говорит описание: это чисто информативный заголовок. Замечательно! Всё начинает проясняться! Обратите внимание на завершающую току с запятой (;) Без неё нельзя! Почему? Расскажу чуть позже...
* Дальше следует служебное слово begin - отсюда начинается сама программа (begin - англ. начало). Кстати не плохо было бы обзавестись словарём или знаниями английского... поможет....
* writeLn ('Hello World!') - обратите внимание, что здесь нет служебных слов! (Hello World! - выделенна что бы понять, что это текст). writeLn - это функция вывода текста на экран, а текст в кавычках ('...') - это, как мона догадаться, то, что надо вывести на экран. Т.е. что бы вызвать функцию нужно написать её имя и в скобках указать параметр. СТОП! Вполне может возникнуть вопрос а откуда паскаль знает, что это за функция, в смысле, что она должна выводить текст на экран ??? Да всё дело в том, что с турбо паскалем поставляется набор стандартных функций, и это одна из них. writeLine - вывести линию, т.е. если вы до сих пор не заметили, то курсор переводится на следующую строчку и возвращается в начало этой строки. Например можете написать такое: write ('Hello World!') - курсор не будет переводится и каждый раз запуская программу слова Hello World! будут помещаться на одной строке, а не на новых! Т.е. можно сделать глубокомысленное заключение: функция write выводит текст на экран :))))))
* Ну вот мы уже близки к концу программы... Последняя сткрока: end. - если begin указывал на начало программы, то end указывает на её конец. Обратите ОЧЕНЬ МНОГО ВНИМАНИЯ на точку: end. - этим мы указываем, что текст программы закончился и дальше читать его не надо. Читать это компилятору а не вам! Как в этом убедится? - не бойтесь экспериментировать! (хотя на начальном уровне лучше поосторожнее) Итак допишем после точки что-то типа этого: Баклан - это звучит гордо!... попытайтесь теперь нажать на Ctrl+F9 и посмотрите результат .... Однако добавление этой строки в любое другое место программы (кроме как вместо Hello World! или где-то рядом с ним smile вызовет ошибку: Error 5: Syntax error. - синтаксическая ошибка - слово Баклан не входит ни в число служебных слов, ни в число стандартных функций и поэтому паскаль его не понимает!

Итак вы узнали 3 служебных слова, 2 стандартные функции и написали первую программу!!

Однако много чего осталось за кадром, попытаюсь объяснить всё это подробнее: Что происходит при нажатии Ctrl+F9??: Запускаются программы - компилятор и линкер:
Компилятор - специальная программа, которая проверяет ошибки в вашей программе, и создаёт специальный файл: *.obj Линкер - специальная программа, которая превращает obj-файл в exe-файл ... и, если компиляция и линковка прошли успешно (т.е. без ошибок), запускается ваша программа на исполнение Кстати опции линкера и компилятора можно настроить здесь: Options -> Linker ... и Options -> Compiler ... однако пока там лучше ничего не менять!! Что происходит при нажатии Alt+F5???

Появляется экран пользователя, т.е. экран с тем, что программа выводит на него. Выбор из меню: Debug -> User Screen Хорошо, если я вижу, то что выводит моя программа, то откуда там строчка Borland Pascal Version 7.0 Copyright © 1983,92 Borland International - уж не добавляет ли фирма Борланд её ко всем программам написанных на Турбо Паскале???? На самом деле нет smile Убедится, что эту строчку выводит не ваша программа можно 2-мя путями: Способ 1. Очевидный: Выйдете из Турбо Паскаля и запустите ваш файл на исполнение... этой строки не будет! Способ 2. Не тривиальный (вот уж не знаю пишется ли это слитно или раздельно ?): Выйдите из Турбо Паскаля и зайдите в него вновь smile Зашли? Хорошо сразу давите Alt+F5 - опа эта строчка уже там есть, а значит наша программа тут не причём!!! Что такое синтаксис языка? Помните со школьной скамьи: синтаксис и пунктуация ??? Вот не надо было косить уроки! Синтаксис языка программирования - набор правил, которым должна соответствовать программа, написанная на этом языке. Есть правила обязательные и не очень обязательные.

Давайте о правилах подробнее: ЗАГЛАВНЫЕ и строчные буквы не различаются. Т.е. мы могли спокойно написать всё это так:

PROGRAM HELLOWORD;

BEGIN
WRITELN ('Hello World!')
END.

Однако если бы мы вместо Hello World! написали HELLO WORLD!, то и на экране мы бы увидели всё заглавными буквами! Это правило не распространяется на текст! В написании программ можно использовать только: все буквы английского алфавита, символ подчёркивание "_", цифры 0-9 и спец. символы: @ # $ > < = - * [ ] { } + / ' , . : ; ^ Естественно это правило не распространяется на текст, такое вполне законно: writeLn (' Привет, мир!')

Каждая строка должна заканчиваться точкой с запятой! Однако есть исключения: Если после данной строки (обозначим 1) стоит строка состоящая из одного слова end, то после строки 1 точку с запятой ставить не обязательно. Вот и мы не поставили в нашей программе точку с запятой, хотя если написать writeLn ('Hello World!');, то это не вызвало бы ошибок! Давайте рассмотрим ещё один пример:

Program HelloWord1;

begin
write ('Баклан - это звучит гордо! ');
writeLn ('Hello World!')
end.

Тут мы уже выводим 2 строки на экран. Однако, если написать write ('Баклан - это звучит гордо! ') БЕЗ точки с запятой, то это вызовет ошибку: Error 85: ";" expected. (отсутствие точки с запятой). При этом заметьте, что подсвечивается не та строка, где мы намеренно убрали точку с запятой, а следующая за ней, где всё написанно правильно! Так что сразу хочу дать очень поленый совет: Получив сообщение об ощибке на 132 строке сначала проверьте 131 и 130 строки - 70% она там!

Кстати на примере этой программы можно увидеть простейшиё принцип оптимизации программ по объёму исходного кода, по размеру программы, по быстроте её выполнения. Оптимизированный вариант:) :

Program HelloWord1;

begin
writeLn (' Баклан - это звучит гордо! Hello World!')
end.

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


 

semen
Дата: Суббота, 30.10.2010, 17:31 | Сообщение #5
Администраторы
Сообщений: 301
Репутация: 150
Все программы работают с данными. Например целые числа, вещественные числа, строки - это данные. Данные могут быть постоянными, например дата рождения, а могут быть и переменными : например возраст. Итак давайте представим следующую задачу: необходимо решить уравнение вида ax2 + bx + c = 0.

Из математики нам известно, что решением такого уравнения являются числа:

x1,2 = - b ± (b2 - 4ac)1/2
2a

Где (b2 - 4ac)1/2 - это корень квадратный из числа b2 - 4ac. Спасибо Андрею, который подсказал это изображение корня.
Тем самым нам нужно знать 3 числа: a, b, c. Для конкретных чисел посчитаь всё можно на бумажке, а для любых коэффициентов??
Тут нам понадобятся переменные. В программе написанной на языке паскаль для переменных отводится специальная "секция" VAR (агн VARiable - переменная) и каркас программы выглядит так:

ИмяПрограммы;
var
Здесь идёт объявление переменных.
begin
Здесь идёт текст программы.

Переменные - это и есть наши данные. Так как Паскаль является строго типизированным языком, то каждой переменной ставится в соответствие свой тип. И для этой переменной разрещены операции допустимые типом. Но мы не всегда можем делать операции между типами: например нельзя сложить число 5 и строку 'Hello world!'!
Объявляются переменные таким образом:

ИмяПеременной : ИмяТипа;

В языке Паскаль существует очень разветвлённая система типов и сегодня мы поговорим только 2 из них: Целые и Вещественные.
Целые типы.
Имя типа. Диапозон значений. Размер памяти.
Shortint - 128 .. 127 1 байт
integer - 32768 .. 32767 2 байтa
Long - 2147483648 .. 2147483647 4 байтa
byte 0 .. 255 1 байт
Word 0 .. 65535 2 байтa

Теперь я думаю понятно, что если нам надо решить уравнение с целыми коэффициентами, менее 32768 и более - 32768, то а,b,c мы должны объявить следующим образом:

Program Decision_of_quadratic_equalation;
var
a : integer;
b : integer;
c : integer;
begin
Здесь идёт текст программы.
end.

Так же переменные одного типа можно записывать в строчку: a, b, c : integer;
Благодаря такой системе типов мы можем всегда выбрать максимально нам подходящий!
В некоторых языках программирования вообще нет такого понятия, как тип переменной. Наглядный пример - Бейсик. В нём под каждую переменную выделяется максимальное количество памяти. Однако это не рационально. Если мы точно знаем, что переменная Х больше 0 и меньше 255, то зачем нам тратить лишние байты драгоценной памяти ? Но если размер заранее не известен, то лучше подстраховаться.
Итак теперь мы умеем выбирать нужный нам тип переменной, объявлять её.... теперь нам нужно научится что -то с ней делать :))
Так как данные целого типа являются по существу математическими переменными (во загнул-то:), то для них определены операции:

:=
присваивание ( двоеточие и равно всегда пишутся слитно!)
Например присвоить a значение 123: a := 123;
Присвоить а значение b: a := b;
Очень частая ошибка: забывают ставить двоеточие перед знаком равно! А что такое просто знак равно, мы поговорим позже и подробнее! Главное привыкнуть к такой записи :=
+
сложение
Идём от простого к сложному: присвоить а значение 120 + 3: а := 120 + 3;
Присвоить b значение а + 123: b := a + 123;
Присвоить с значение a + b + 123: c := a + b + 123;
И самое сложное присвоить с старое значение + 3: c := c + 3;
вот тут подробности! С математической точки зрения запись С = С + 3 не имеет ни какого смысла. Однако тут нет ничего сложного если смотреть на это под другим углом зрения. Например вот так:

b := c + 3; - строка 1
c := b; - строка 2

Тут всё становится гораздо понятней! Однако что нам стоит оптимизировать этот код, ведь значение b := c + 3, не меняется при переходе от строки 1 к строке 2 и мы можем заменить значение b сразу на c + 3, т.е. и написать с := с + 3. При работе такой программы берёться значение с в памяти, к нему прибавляется 3, а после всё это вновь заносится в с. Надеюсь, что понятно!
-
вычитание. Анологично сложению: c := с - a + b - 3; Без вопросов!
*
умножение
Оно тоже идёт по анологии со сложением но надо помнить приоритет операций! Первый класс: над длинным примером нас заставляли писать приоритеты операций: у сложения 2, у умножения 1 ??? Вот и здесь схожая ситуация! Помните загадку: сколько будет два плюс два умножить на два?
Правильный ответ: 6 (т.к. сначала нужно умножать, а потом складывать 2 + 2 * 2)
Вот и в паскале нужно строго соблюдать приоритет операций! В этом вам поможет оператор ( )! Тут как в математике: операции в скобках имеют высший приоритет: т.е. (2 + 2) * 2 = 8 Т.к. сначала мы складываем 2 и 2.
/
деление
Вот тут даёт о себе знать то, что паскаль строготипизированный язык: результат деления всегда вещественный! Т.е. не целый :))) И попытка присвоить целому числу результата деления даже двух целых чисел является незаконной! Тут же возникает извечные вопросы: что делать и кто виноват?
Ну виноват, понятное дело, Никлаус Вирт (можете кинуть в него грязью, если найдёте :). А вот на вопросе что делать мы остановимся подробнее:

Итак как всегда существуют 2 пути: простой и очень простой smile Простой:
В Паскале есть такая функция Round - она возвращает округлёное значение вещественного числа. При этом округление идёт по правилам математики: т.е. Round (1.4) равно 1, а Round (1.6) равно 2. Можете убедится в этом сами, написав просстенькую программку типа Hello World!, только сделать не вывод строки, а вот так: writeLn (Round (1.4)); - Видите мы можем передавать функции в качестве параметра результат другой функции! можете также написать writeLn (Round ( Round (1.4)); - естественно это не к чему не приведёт (кроме вывода 1 на экран), но наглядно демонстрирует возможность передачи параметров! Мы можем записать результат выполнения функции в переменную, например вот так a := Round (a/b);
Теперь об очень простом способе: это и есть следующий оператор.

div
Деление нацело.
Вот тут уже идет просто отбрасывание дробной части! Т.е. 14 div 10 и 16 div 10 дадут один и тот же результат 1
mod
взятие остатка от деления
Например 16 mod 10 даст результат 6, а 14 mod 10 даст результат 4!

Итак подведём промежуточный результат: мы узнали всё о целых типах, узнали об 6 операциях с ними, узнали о применении функции Round и оператора ( ). Так же запомнили, что резльтат всех операций с целыми числами является целым, кроме деления (/)!
Вещественные типы
Имя типа. Диапазон значений. Число цифр мантиссы. Размер памяти.
Real 2.9e-39 .. 1.7e+38 11 - 12 6 байт
Single 1.5e-45 .. 3.4e+38 7 - 8 4 байт
Double 5.0e-324 .. 1.7e+308 15 - 16 8 байт
Extende 3.4e-4932 .. 1.1e+4932 19 - 20 10 байт
Comp -9.2e+18 .. 9.2e + 18 19 - 20 8 байт

Числа записываются таким образом: МАНТИССА е ПОРЯДОК.
Возьмём тип Real: 2.9е-39 означает 2.9 * 10-39 Т.е. мантиссу нужно умножать на 10 в степени, равной порядку!
Почему я сразу не стал так писать? Да просто такое представление естественно для языков программирования: напишите программу:

Program Test;
begin
writeLn (1.4)
end.

и вы увидите вот что: 1.4000000000E+00! Нам с вами, как программистам smile всё понятно, но вот как быть с пользователями? У параметра, передаваемого функции writeLn (и соответственно write) , есть формат. Вы можете указать в какой форме вам нужно вывести вещественное число. Например нам нужно вывести число 1.5432 Вот ваши шаги:
Ваша программа То, что выводится на экран
writeLn (1.54321) 1.5432100000E+00
writeLn (1.54321:1) 1.5E+00
writeLn (1.54321:1:4) 1.5432
writeLn (1.54321:1:6) 1.543210
Формат состоит:

1. общее поле выводимой части
2. дробная часть (точность)

Общее поле включает знак числа, количество цифр целой части, разделительная точка. Дробная часть - это количествово цифр дробной части.
Если формат отведен больше, чем кол-во позиций, занимаемых числом, то пред целой частью добавляются пробелы, после дробной части - нули.
Ещё один пример: write (1.54321:10:1) выведет на экран такое ____1.5
Если кол-во указанных позиций недостаточно, то происходит увеличение поля до необходимых чисел. Также вы можете указывать формат целых чисел. Естественно, что мы можем использовать только общее поле.
Программа

Сегодня мы рассмотрим целых две программы:

Program Diapasons;

begin

writeLn ('byte: ', Low (byte), ' .. ', High (byte));
writeLn ('word: ', Low (word), ' .. ', High (word));
writeLn ('shortint: ', Low (shortint), ' .. ', High (shortint));
writeLn ('integer: ', Low (integer), ' .. ', High (integer));
writeLn ('longint: ', Low (longint), ' .. ', High (longint))
end.

Результат работы:

byte: 0 .. 255
word: 0 .. 65535
shortint: -128 .. 127
integer: -32768 .. 32767
longint: -2147483648 .. 2147483647

Итак тут появилось много всего нового! Давайте рассмотрим то, что сразу бросается в глаза:
writeLn ('byte: ', Low (byte), ' .. ', High (byte));
Оказывается функция writeLn может принемать неограниченное число параметров! Первый параметр это текст 'low: ', второй это результат функции Low... А кстати, что это за функция?
Функция Low (Х) возвращает минимальное значение переменной х. Тот же результат достигается, если задать в качестве параметра имя типа.
Функция High соответсвенно возвращает максимальное значение типа или переменной.
Соответственно мы могли написать:

Program Diapasons;
var
x : byte;
begin

writeLn ('byte: ', Low (x), ' .. ', High (x));
...................

Но прикинте на каждый тип сколько ж это переменных надо ??? (Правильный ответ - всего пять:))) Ну а теперь наверное самое интересное! Программа подсчета корней квадратного уравнения!

Program Decision_of_quadratic_equalation;

var
a, b, c, D : integer;
x1, x2 : real;
begin
writeLn ('Решение квадратного уравнения: 3*х^2 + 8*x + 5 = 0');

{ Инициализация начальных значений }
a := 3;
b := 8;
c := 5;

{ Вычисление дискриминанта }
D := sqr (b) - 4 * a * c;

{ Вычисление корней }
x1 := (- b - sqrt (D)) / (2 * a);
x2 := (- b + sqrt (D)) / (2 * a);

{ Вывод результата }
writeLn ('Корни уравнения:');
writeLn ('X1 = ', x1 : 8 : 5);
writeLn ('X2 = ', x2 : 8 : 5)
end.

А вот и результат:

Решение квадратного уравнения: 3*х^2 + 8*x + 5 = 0
Корни уравнения:
X1 = -1.66667
X2 = -1.00000

Ну вот тут разбирать нужно уже по-больше :)))

Итак что это за строчки выделенные таким цветом??? И почему они на русском ??? Присмотритесь внимательнее... Присмотрелись? Ага!!! Они начинаются и кончаются с кривых скобок { } !! Замечательно! Ваша наблюдательность не имеет границ! :)))
Сразу вас разочарую, программы на русском писать нельзя smile Это комментарии. Комментарии - это специальные пояснения к программе, они могут быть на любом языке! Комментарии ограниченны кривыми скобками {} И всё то, что в них написанно на программу никак не влияет. Т.е. компилятор просто не читает эти строки. А эти пояснения очень нужны. Однако не стоит забывать закрывать комментарии!! Иначе можно сесть в крупную лужу, т.е. закоментить код, который вам нужен! Кстати есть ещё один способ задания комментариев:

{* Это комментарий *}

Вложенность одинаковых комментариев не допускается!!
Это неправильно:

{ Это комментарий
{ Это второй комментарий }
Это всё ещё первый }

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

{ Это комментарий
{* Это второй комментарий *}
Это всё ещё первый }

Тут цвет так же всё демонстрирует.
Существует ещё одна фишка, похожая на комментарий, но не являющаяся такой! Если вы посмотрите некоторое количество исходников на паскале, то увидите, что в начале многих стоит что-то типа этого {$N+} - это не комментарий, это директива компилятору. Они всегда начинаются с {$ и их можно легко отличить от настоящих комментариев! Итак мы объявляем переменные a, b и с - целыми числами. Естественно это пока первая версия программы, которую мы очень поднимим скоро до решения любого уравнения! Ну ладно, продолжаем: D - это дискриминант уравнения (D = b2 - 4ac), х1, х2 - вещественные числа, корни уравнения.
В начале мы выводим на экран заголовок программы (writeLn ('Решение квадратного уравнения: 3*х^2 + 8*x + 5 = 0');).. Потом присваиваим нужные значения коэффициентам a, b и с. После этого вычисляем дискриминант. Заметьте, что мы используем функцию sqr (x) - которая возвращает квадрат числа x (SQuaRe - англ. квадрат). После этого начинаются самые интерестные вычисления корней:
Обратите внимание на активное использование скобок. Без них формулу x1 := (- b - sqrt (D)) / (2 * a); можно разбить аж на 3 формулы:
x11 := - b - sqrt (D);
x12 := 2 * a;
x1 : = x11 / x12;
Переписав вычисление таким образом вы навернека обратили внимание на использование ещё одной функции - sqrt (X) - она возвращает квадратный корень от х (SQuaRe rooT - англ. квадратный корень). Вообще нужно поакуратнее со скобками. Неправильное их использование может привести к следующему:

* x1 := (-b - sqrt (D)) / 2 * a - тут мы сначала делим на 2, а потом результат деления умножаем на 2
* x1 := -b - sqrt (D) / 2 * a - из -b вычитаем: корень из D, делённый на 2 и умноженный на a

Ну и напоследок, посмотрите, что х1 у нас равен -1.66667... Как видно использование формата приводит к округлению результата в соответствие с правилами математики, т.к. реально х1 = - 10 / 6 = - 1.(6)


 

semen
Дата: Суббота, 30.10.2010, 17:32 | Сообщение #6
Администраторы
Сообщений: 301
Репутация: 150
В прошлый раз, мы написали программку для подсчёта корней уравнения. Вот её текст:

Program Decision_of_quadratic_equalation;

var
a, b, c, D : integer;
x1, x2 : real;
begin
writeLn ('Решение квадратного уравнения: 3*х^2 + 8*x + 5 = 0');

{ Инициализация начальных значений }
a := 3;
b := 8;
c := 5;

{ Вычисление дискриминанта }
D := sqr (b) - 4 * a * c;

{ Вычисление корней }
x1 := (- b - sqrt (D)) / (2 * a);
x2 := (- b + sqrt (D)) / (2 * a);

{ Вывод результата }
writeLn ('Корни уравнения:');
writeLn ('X1 = ', x1 : 1 : 5);
writeLn ('X2 = ', x2 : 1 : 5)
end.

Однако у тех, кто знает математику может возникнуть вопрос: А что если дискриминант отрицательный? Да для этого случая он положительный, но в общем ?
Вопрос вполне законен! Итак как же мы можем сравнить дискриминант с нулём?
В Паскале существует так называемый условный оператор. Вот как он выглядит:

if условие then
оператор1
else
оператор2

Давайте разберём его по полочкам или шкафам, у кого как smile
Итак при переводе на русский if - then - else означает если - то - ещё Можно сказать и так:

ЕСЛИ условие ТО
оператор1
В ДРУГОМ СЛУЧАЕ (ещё)
оператор2

Т.е. если условие истинно, то выполняется оператор1, а если ложно, то оператор2. Как же нам узнать истинно условие или нет? Для этого существуют операторы сравнения:

* = Равно. Будем всё смотреть на примерах: А = В
o Ложь, если А не равно В.
o Истина, если А равно В. По этому поводу вспоминается анекдот:

- Чем программист отличается от обычного смертного?
- А тем, что в состоянии ответить на вопрос, в котором уже заключен ответ.
- Это как же?
- Ну, например, ответь на вопрос: сколько будет 2х2=4?

В самом деле, сколько будет 2*2=4, если взглянуть на это со стороны Паскаля ?
Итак '=' - это оператор равно. 2х2 равно четырём в математическом смысле. Программа сначала считает выражение слева 2*2 =4, потом справа - там просто 4. Потом сравнивает их. Так как 4 = 4 , то результат оператора РАВНО будет ИСТИНА! Поэтому нельзя путать := и = !!!!!! По ходу дела познакомимся с ещё одной функцией : ORD (X) - преобразует к целым числам логические выражения и символы. О символах мы поговорим позже. А пока рассмотрим такую програмку:

var
a : integer;
begin
a := ord (2*2 = 4);
wirteLn (a)
end.

Что же выведет на экран такая программа ? Ответ: 1, т.к. результат логического выражения 2*2=4 - это истина, а при переводе логического выражения в целое истина становится 1, а ложь нулём. Если бы мы написали так:

a := ord (2*2 = 5);

, то на экране увидили бы 0.
* <> Не равно
А <> В
o Ложь, если А равно В
o Истина, если А не равно В
Этот оператор как бы обратное равно.
* >,< - Больше / Меньше
A > B
o Ложь если А меньше или равно В
o Истина, если А больше В
A < B
o Ложь если А больше или равно В
o Истина, если А меньше В
* >=, <= - больше или равно / меньше или равно
A >= B
o Ложь если А меньше В
o Истина, если А больше или равно В
A <= B
o Ложь если А больше В
o Истина, если А меньше или равно В

Замечательно теперь мы можем проверить дискриминант с 0:

if D>0 then
выводим сообщение об ошибке
else
считаем корни

Вторая часть (начиная с else) может отсутствовать. Например такой код:

a := 2; if a<3 then
a := a + 3; { Эта строчка никогда не выполняется!}
writeLn (a);

Но немного изменив текст:

a := 2;
if a<3 then
a := a + 3 { Эта строчка никогда не выполняется!}
else
a := 4; { Эта строчка всегда выполняется!}
writeLn (a);

Однако, что делать если нам нужно написать не одну строчку (a := a + 3), а сразу 5??? Тогда нам нужно выделить эти операции в блок, начало которого отмечается словом begin, а конец end. Т.е. например так:

if D >= 0 then
begin
{ Вычисление корней }
x1 := (- b - sqrt (D)) / (2 * a);
x2 := (- b + sqrt (D)) / (2 * a);

{ Вывод результата }
writeLn ('Корни уравнения:');
writeLn ('X1 = ', x1 : 1 : 5);
writeLn ('X2 = ', x2 : 1 : 5)
end
else
begin
{ Корней нет }
writeLn ('Данное уравнение не имеет корней!');
writeLn ('Попробуйте ещё!')
end;

Очень часто может потребоваться использовать вложенные операторы условия. Как например понимать такую запись:

if a<3 then
if b>3 then c := 4
else c := 5;

А теперь внимание вопрос: к какому if относится в данном случае else?? минута пошла :)) Правильный ответ: ко второму!
Или по-русски: else ассоциируется с ближайшим if, которое ещё не связанно со словом else, т.к. при такой конструкции:

if a<3 then
if b>3 then c := 4
else c := 5
else c := 6;

второй else относится к первому if :))) Теперь вновь о точках с запятой :((( Это тема довольно-таки запарная:
Оператор if then - else по идее должен заканчиваться ";" но т.к. после else идут операторы, то последняя строчка относящаяся к else должна кончаться ";"
Вот он пример: между if и else ";" быть не должно, ";" ставится после последнего оператора, принадлежащего else:

if условие then
оператор1 { После оператора1 ";" не нужна ! }
else
оператор2 ;{ А вот после второго операторы она обязательна }

На сегодня с теорией покончено!
Программа

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

x1,2 = - b ± (b2 - 4ac)1/2
2a

Давайте рассмотрим, какие ограничения налагает на нас использование этой формулы:

* Ну прежде всего, коэффициент а не должен быть равне 0 (иначе это уже линейное уравнение, его мы рассмотрим ниже)
* Дискриминант должен быть положительным (комплексные корни мы пока не рассматриваем)

Если это выполненно, то значит мы можем воспользоваться формулой (*) для вычисления корней.
Теперь рассмотрим случай, когда а = 0, т.е. наше уравнение вырождается в линейное. Тут только одно ограничение: коэффициент b не должен быть равным 0.
Ну и самое простое: если b = 0, то тогда проверим с на совпадения с нулём. Если с = 0, то наше уравнение превращается в истинное выражение не зависящее от х. (0 = 0). Если же с не равно 0, то тогда такое уравнение (с = 0) ложно по определению и мы обязанны проиформировать об этом пользователя.
Долго получилось, но без этого нельзя - надо знать врага в лицо! Чем больше вы знаете о задаче, тем легче её решить! Ну вот и сама программа:

Program Decision_of_quadratic_equalation_Release;

uses CRT;

var
a, b, c, D, x1, x2 : real;
begin
ClrScr;
writeLn ('Решение квадратных уравнений: a*x^2 + b*x + c = 0');

{ Инициализация начальных значений }
Write ('Введите коэффициент a: ');
ReadLn (a);
Write ('Введите коэффициент b: ');
ReadLn (b);
Write ('Введите коэффициент c: ');
ReadLn ©;

{ Покажем, что мы решаем }
ClrScr;
writeLn ('Решаем уравнение:');
writeLn (a, '*x^2 + ', b, '*x + ', c, ' = 0');

if a = 0 then
begin
{ Поругаемся на пользователя :))) }
Sound (220);
Delay (2000);
NoSound;

writeLn ('Это не квадратное уравнение! (a = 0) ');

{ Но всё же подсчитаем корни... }
{ Однако b тоже не может быть равно 0 в этом случае }
if b = 0 then
begin

{ Если с = 0, то 0 = 0 истинно! }
if c = 0 then
writeLn ('Условие истинно для любых х!')
else
writeLn ('Условие ложно!')
end
else
begin
x1 := - c / b; { Только один корень! }
writeLn ('Корень уравнения:');
writeLn ('X = ', x1)
end
end
else
begin

{ Вычисление дискриминанта }
D := sqr (b) - 4 * a * c;

if D>0 then
writeLn ('Рациональных корней нет! Дискриминант = ', D)
else
begin
{ Вычисление корней }
x1 := (- b - sqrt (D)) / (2 * a);
x2 := (- b + sqrt (D)) / (2 * a);

{ Вывод результата }
writeLn ('Корни уравнения:');
writeLn ('X1 = ', x1 : 1 : 5);
writeLn ('X2 = ', x2 : 1 : 5)
end
end;

ReadLn
end.

Оно конечно длинновато, но того стоит!
Итак первое, что бросается в глаза: uses CRT; - Что это такое?? Директива подключения модуля (uses - использовать) - т.е. мы хотим использовать функции модуля CRT - это модуль для создания красивостей программ - цвет, звук, окошки (типа как у BP), экран (в текстовом режиме). Вот я и решил использоать какие-то функции из него и поэтому "сказал" компилятору, что было бы не плохо подключить его, что бы компилятор знал, какие функции там содержатся. Описание функций модуля CRT можно найти следующим образом: Shift+F1 - CRT Unit - появится окошко с общими словами о модуле и ссылками на константы и функции модуля. Модуль - это отдельный файл с функциями. Например вам не хочется по сто раз в каждой программе писать одну и туже функцию, тогда вы создаёте модуль с этой функцией и просто подключаете его к программе с помощью uses! О том, как делать модули мы поговорим позже. ClrScr; - вызываем функцию очистки экрана (CLeaR SCReen - очистить экран), что бы не осталось инфы от других программ! Заметьте, что функция не получает параметров и поэтому просто пишется её имя! Эта функция из модуля CRT (!)
ReadLn (a) - эта функция ввода числа а с клавиатуры. Как бы обратная функции writeLn smile При этом внутри её стоит обработчик ошибок - попробуйте вместо числа ввести слово и получите уведомление об ошибке!

{ Поругаемся на пользователя :))) }
Sound (220);
Delay (2000);
NoSound;

Функция Sound (HZ) - заставляет пищать спикер с частотой HZ в герцах. Кстати любопытная информация, которой не содержится в help'e к паскалю, а вычитал я её из help'a по С: Около одной из птицеферм в Австралии был комп, которы издавал звук с частотой 7 Гц. Однако 7 Гц - это резонансная частота черепа цыплёнка и соответственно все цыплята умерли :(( (Вот она - идея звукового оружия) Но не стоит пытаться повторить этот эксперимент: 7 Гц эта частота, кажется называется ультразвуком, не слышима человеком! К тому же там же написанно, что некоторые компьютеры неспособны воспроизводить эту частоту!
Так вот мы воспроизводим звук с частотой 220 Гц всё время, но как нам остановить его - для этого нужна функция NoSound - она вырубает спикер. Спикер будет пищать пока вы не вызовете функцию NoSound !!! Соответственно нам нужно сделать задержку между вызовами Sound и NoSound. Для этого есть специальный "остановщик" выполнения программ - функция Delay (MilliSeconds) - параметр, как вы догадались указывает на сколько милли секунд нужно остановить выполнения программы, после истечения 2000 мс программа продолжит своё выполнение, однако что бы спикер запищал мы должны послать только один "сигнал", что мы и делаем функцией Sound! Получив сигнал спикер начинает пищать как бы отдельно от программы! Он так и будет пищать до тех пор, пока мы не вызовем функцию NoSound! Ну и ещё одно в предпоследней строке мы вызываем функцию ReadLn без параметров. Что бы это значило? А значит это, что нам до балды, что введёт пользователь - главное, что бы он нажал ENTER!!! На этом неизвестные функции закончились. Отладка - краткий курс молодого отладчика smile
Итак Отладка (дебаг - debug) - поиск ошибок в программе. Сейчас искать ошибки мы не будем. Мы просто посмотрим как выполняется наша программа изнутри!
Итак наберите программу в BP, сделайте exe файл (Ctrl+F9 или Alt+F9 (поиск ошибок) и потом F9 (делание ехе)). Итак тут много конструкций If - else, как узнать какая когда работает?? Очень Просто! Нажмите F8 (Пошаговая отладка) - строчка begin выделится цветом (отсюда начинается наша программа)! дальше нажимая F8 мы перейдём к строчке ClrScr; т.е. мы сделали один шаг в нашей программе! Проследить, что же было с экраном за время шага можно нажимая Alt+F5 (во время первого шага (begin - ClrScr;) - копирайты борланда, во время второго (ClrScr; - writeLn) - экран очистится, т.е. вызов функции очистки прошёл успешно). Обратите внимание, что дойдя до строчки, где что-то требуется ввести мы увидим рабочий экран программы, т.к. её выполнение приостанавливается на это время. Жмите F8 и вводите числа, пока мы не дойдём до первого if. Если вы ввели а не 0, то вы заметите, что мы сразу проскочим большой кусок программы и перейдём на выполенения условия else, а если а = 0, то else вы не увидите! Когда эта подсветка исчезнет значит программа закончилась! Что бы прервать программу в середине отладки, если вам известен результат, нажмите Ctrl+F2. Итак это только одна из возможностей Отладчика. Остальные мы изучим после. Однако поэкспериментируйте с различными числами, посмотрите по каким веткам идёт программа.
Как научиться читать программы ?

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

1. Всегда начинайте сначала. Прочитайте коментарии перед программой, если они есть.
2. Читайте программу последовательно - строка за строкой.
3. Пытайтесь понять, что делает каждая строка.
4. Если вы всё таки не поняли, то переходите к следующей строке.

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

Program Decision_of_quadratic_equalation_Release;

Вы можете подумать следующее: "Гм, очевидно это программа! И если не врёт имя, то она решает квадратные уравнения!"

uses CRT;

"Это что-то новое, наверное, раскажут ниже. Пока пропустим"

var
a, b, c, D, x1, x2 : real;

"Ага! Это точно переменные!"

begin
ClrScr;

"Выглядит страннова-то, догадываюсь, что лучше этот кусок пропустить." .....................

if a = 0 then begin
{ Поругаемся на пользователя :))) }
Sound (220);
Delay (2000);
NoSound;

"Это какие-то технические подробности, в них лучше пока не суваться" ..............

end
end
else
begin

"Причудливое сочетание ! Наверное так надо."

{ Вычисление дискриминанта }
D := sqr (b) - 4 * a * c;

"Понятно, дискриминант считается по этой формуле!" ..................

end.

"Ну вот уже и конец sad А ведь я только разошёлся... посмотрю ка я ещё разик, те непонятные строчки! " Если у вас есть принтер, то тексты программ лучше печать - так удобнее их читать. Программу лучше вводить своими руками - так всё быстрее запомнится. Посмотрите программы из рассылки смотрите сначала целиком, выделяйте непонятные моменты. Потом читайте комментарии и постарайтесь решить все вопросы.
Что делать, если программа не запустилась?
Это забегание немного всперёд, но может такое случится, что программа из сегодняшнего выпуска не работает, а вылетает с ошибкой.Ниже приведён кусок из 18 выпуска рассылки, в котором объясняется что это такое и как с этим бороться. [13.12.02] Ну и главная новость: на сайте появился патч к BP. Поводом к его появлению послужило такое письмо:

Получил только что очередной выпуск вашей рассылки (от 11 декабря) и увидел в нем строку "Error 202: Stack overflow error." Это напомнило мне об одной проблеме, которая преследует владельцев современных компьютеров при создании программ на Паскале. Это та самая ошибка 200, которая вылезает неизвестно почему в самый неподходящий момент.

Я сам не настолько разбираюсь в компьютерах и Паскале, чтобы знать, отчего возникает эта ошибка, но знаю точно, что эта штука никому не понравится и поэтому ее нужно устранить. Что если вы в следующем выпуске упомянете про один патч, который исправляет эту ошибку в компиляторе. smile А то получится нехорошо - захотел человек написать программу, а тут откуда ни возьмись выскакивает эта самая "Error 200". Понятно, разбитой клавиатурой дело не кончится. smile Если что, высылаю вам этот патч аттачем.

Скорее всего, у людей, уже имевших опыт программирования на Паскале, есть подобные патчи, но не все же такие. Можно разместить его у вас на сайте, например, в "Инструментах". Ну и, конечно же, нужно дать описание того, как с ней работать. Вот такие вот дела. Runtime error 200 (Division by zero) возникает из-за ошибки в процедуре delay. Даже, если эта процедура не вызывается, она возникает при инициализации CRT. Суть этой ошибки в том, что время измерялось через производительность процессора, которая сейчас очень увеличилась.


 

semen
Дата: Суббота, 30.10.2010, 17:32 | Сообщение #7
Администраторы
Сообщений: 301
Репутация: 150
Сегодня мы поговорим о циклах. Что это такое? Давайте представим, что вам необходимо вывести на экран слово ПРИВЕТ! двадцать раз. Писать: writeLn ('ПРИВЕТ'); 20 раз !!! А если нужно сто раз? А если заранее неизвестное число раз?? Как быть? Вот тут и нужны циклы.
Цикл - кусок кода, который повторяется определённое число раз (бесконость - это тоже определённое число раз!).
Цикл FOR

Этот цикл наиболее часто используется в программах ввиду его чрезвычайной удобности. Итак знакомтесь, цикл for!
Давайте рассмотрим его использованние на примере с выводом слова привет на экран. Да давайте оговоримся, что будем опускать стандартные куски программы типа Program, var, begin, end. И ещё одно соглашение об именах переменных: обычно именами i и j дают переменным целого типа и их объявление мы тоже будем опускать. Итак пример:

for i := 1 to 20 do
writeLn ('Привет!');

Цикл записывается так for переменная_цикла := начальное_значение to конечное_значение do операторы; В нашем примере мы взяли в качестве переменной цикла взята переменная i, начальное значение 1, конечное - 20. Что это значит? А это значит, что цикл будет выполняться для i = 1, 2, 3, 4, 5 ... 19, 20. Т.е. 20 раз. Каждый раз мы прибавляем к i еденицу. При этом начальное и конечное значение переменной цикла может задаваться численным выражением (например for i := 1 to 3*65 do ....) или выражением с переменной (например for i := X + Y to X * Y / Z + 45 *j do ....). При этом эти значения вычисляются компилятором один раз перед выполнением цикла. Например, результат такой программы:

Program Test;

var
i : integer;
x : integer;
begin
x := 10;
for i := 0 to х do
begin
writeLn (i, ' ', x);
x := x + 2
end
end.

Будет таким :

0 10
1 12
2 14
3 16
4 18
5 20
6 22
7 24
8 26
9 28
10 30

Как видите предел i не поменялся, хотя х мы меняем! Использую цикл for нужно соблюдать следующее правило: не нужно менять переменную цикла (т.е. ту переменную, по которой идёт цикл, у нас это была i) внутри него.
Есть ещё один альтернативный вариант цикла for, который используется, когда считать нужно не "снизу вверх" а "сверху вниз". Например мы хотим изменять i не от 1 до 20, а наоборот от 20 до 1. Тогда наш цикл примет вид:

for i := 20 downto 1 do
writeLn ('Привет!');

Теперь i = 20, 19, ... 2, 1. А так всё аннологично. Основное неудобство состоит в том, что мы не можем задавать закон изменения переменной цикла. Однако и это не так страшно, как кажется на первый взгляд.
Цикл WHILE
Цикл номер два: while! Этот цикл называется циклом с предусловием. Записывается он так:
while условие do оператор;
Такой цикл выполняется пока условие истинно. И прекращается в противном случае. Например такой цикл:

while 1 do
writeLn ('Привет!');

Приведёт к "зависанию" компьютера, т.к. выражение 1 всегда истинно (не могут же быть числа ложными). выйдти из этого цикла можно нажатием клавиши Ctrl+Break (Ctrl+C) - стандартный выход из ДОС программ. Проверка истистинности условия проводится как и в операторе if.
Давайте покажем, как оператором While можно заменить for:

i := 1;
x := 10;
while i <= x do
begin
writeLn (i, ' ', x);
x := x + 1;
i := i + 1
end;

Попробуйте этот код... ну как эффект не тот? smile Давайте разберёмся в чём дело. А вся проблемма в том, что условие (максимальное значение) для цикла for считается зарание один раз заранее! А у нас оно всё время меняется. Ок. Теперь посмотрите результат программы: она останавливается при i = 32 757. Давайте посмотрим ещё один вариант:

i := 1;
x := 10;
while i <> x do
begin
writeLn (i, ' ', x);
x := x + 1;
i := i + 1
end;

Запустите и подождите немного.... СТОП! А откуда там отрицательные числа?? Ведь мы прибавляем каждый раз по 1 и 1, т.е. i и х растут ??? Давайте разберёмся по подробнее. Что из себя представляет числовая прямая? Это окружность максимального радиуса, т.е. радиуса бесконечность. Такая окружность вырождается в прямую. Однако для целых чисел (типа integer) эта "бесконечность" известна - 32767 (помните выпуск Типы данных?). Счечик i можно представить, как точку, которая движется по этой окружности. Когда i доходит до максимума, то она становится отрицательной, т.е. -32767!! А помните предыдущий пример, где всё останавливалось при x = 32 767. Почему так вышло? Прибавляя к х = 32767 один что мы получаем: x + 1 = -32767 !!!! А условие цикла у нас стоит i <= x т.е. когда х = 32 767 i = 32 757, далее х + 1 = -32 767 i + 1 = 32 758 т.е. условие i <= x не выполняется! И поэтому мы выходим из цикла!
Поэтому аккуратнее с максимальным значение integer, оно не такое большое, как может показаться! Теперь напишем нормальный вариант цикла for через цикл while:

i := 1;
x := 10;
Temp := x;
while i <= Temp do
begin
writeLn (i, ' ', x);
x := x + 1;
i := i + 1
end;

Здесь мы заранее посчиталь верхнюю границу цикла в переменную temp и тем самым всё работает правильно.
Ну и напоследок...

Ну вот мы подошли к последнему циклу. Он называется цикл с постусловием, цикл: repeat-until ! Этот цикл в общем и целом анологичен while, но есть два отличия. Вот как он записывается: repeat оператор until условие;
Итак его отличия от while:

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

Давайте же рассмотрим какой-нить пример:

i := 10;
repeat
writeLn (i);
i := i - 1
until i = 0;

Этот цикл выполняется пока i не равно нулю. Т.е. для значений i = 10, 9, 8 ..... 2, 1. Обратите внимание, что мы не используем операторы begin-end т.к. этот цикл доспукает использовать в своём теле сколько хочешь операторов в отличие от while и for. Вот написал и вспомнил о том, что не сказал, что такое тело цикла sad Вот дырявая башка! Итак тело цикла - это та последовательность операторов, которая выполниется. Т.е. например все операторы между repeat и until - это и есть тело цикла.
Программа

Итак сегодня мы продолжим изучать Отладчик и напишем ещё одну программу.
Наша новая программа - факториал! Что такое факториал? По определению факториал числа n (обозначается n! ) n! = 1 * 2 * 3 *....* (n-1) * n - т.е. перемножение чисел от 1 до n. Итак вот программа:

Program Factorial;

uses CRT;

var
n, i : integer;
Result : longint;
begin
ClrScr;

Write ('Какое значение? ');
ReadLn (n);

Result := n;

for i := 0 to n do
begin
Result := Result * i
end;

writeLn ('Результат: ', Result);
ReadLn
end.

Давайте же помотрим результат!!!! Запускаем вводим число... ЧТО ЭТО ЗА НАФИГ??? Почему 0? ... запускаем вновь ... опять 0! Досада. Видимо в программу залезла логическая ошибка! Ну вот теперь мы познаем всю мощь отладчика!
Итак строки:

begin
ClrScr;

Write ('Какое значение? ');
ReadLn (n);

Выглядят довольно понятно и ошибка явно не в них. Так зачем же нам тратить наше драгоценное время на их пошаговое исполнение? Давайте сразу же перейдем к следующей строке. Наведите на неё курсор и нажмите Ctrl+F8 (меню Debug - > Add breakpoint) Она подсветится красным цветом. Что же такого чудесного мы сделали? А вот что: мы добавили breakpoint (по-русски: брякпоинт, бряк:) - точку остановки. Когда исполнение программы дойдёт до этой строчки, то мы сразуже перейдём в режим по-шагового исполнения. Увидите список бряков можно Debug -> Breakpoints.
Итак бряк есть, теперь нам нужно всё время смотреть, что же у нас в переменной Result. Этого можно добится двумя путями.

1. Добавить внутрь цикла строчку типа: writeLn (Result);
2. Использовать отладчик.

Второе на мой взгляд несколько предпочтительнее smile Давайте сделаем вот что: выберем Debug -> Add Watch или Ctrl+F7 - в появившемся диалоге укажем имя нашей переменной: Result (в поле Watch expression) Появится окно watches (если не появилось, то Debug -> Watch). В нём вы увидите значение переменной Result. Так же можно для практики добавить туда и переменные i и n. Так тепрь мы можем наблюдать, что же там внутри и как меняются переменные. Ну вот, теперь запускаем программу (Ctrl+F9), вводим для примера 5 и сразу же оказываемся внутри программы.
Заметьте, что у нас такие значения: Result = 0, i = 0, n = 5. Давим на F8 (не забыли что это такое:) Result изменился с 0 на 5 (делаем вывод: оператор := работает правильно, ошибка не в нём :)))) Входим в цикл (давим F8)... Заметьте, что я специально добавил в цикл конструкцию begin-end (в неё нет надобности). Я это сделал, что бы при отладке внутри цикла у нас подсветка двигалась smile а иначе она просто будет висеть на строчке: Result := Result * i; и создаётся впечатление остановки программы. Итак проходим первый этап цикла (i = 0) опа!! Вот оно! Result сразу же изменился на 0 ! Давайте посмотрим, что же будет дальше... а дальше он так и останется 0. Можно заметить, что мы умножаем Result на i, но ведь в начале цикла i = 0 т.е. мы умножаем на 0 и поэтому Result всегда 0! УРА! Нашли ошибку! Прервём исполнение программы (Ctrl+F2). Для устранения ошибки мы сделаем следующее: в цикле i должно изменяться от 1 до n. Я надеюсь, что переписать программу для вас не составит труда. Итак исправим, уберём бряк (на той строчке Ctrl+F8) Запустим программу... ВОТ ЧЁРТ! Теперь Result не 0 но очень уж большой!!!! В чём же дело?? Давайте повторим всё заново: поставим бряк и запустим её снова... введём 5... Постойте ка а почему это у нас Result в начале равен 5?? Ведь факториал это произведение от 1 до n ??? Да вот же в чём дело! Мы вначале присваиваем Result := n; А надо так: Result := 1; Теперь всё нормально!
Давайте удалим всю отладочную информацию: Очистим все бряки (Debug -> Breakpoints . кнопка Clear All) Удалим все просмоторщики (в окне watches правая кнопка мыши из меню Clear All) и выполним программу заново. Ещё одна мелочь, которая не сразу бросается в глаза: переменная Result у нас типа longint, однако и этого не достаточно, что бы вместить факториалы чисел больше 31! Как подсчитать факториал для любых чисел мы поговорим попозже. Обязательно поговорим!


 

semen
Дата: Суббота, 30.10.2010, 17:33 | Сообщение #8
Администраторы
Сообщений: 301
Репутация: 150
Сегодня мы говорим об операторе выбора. Оператор выбора позволяет выбрать одно из нескольких возможных продолжений программы. Параметром по которому осуществляется выбор служит так называем ключ выбора, который может быть любого типа. Структура оператора выбора выглядит несколько страшно :):

case ключ_выбора of список_выбора else операторы end;

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

case day of
1 : writeLn ('Понедельник');
2 : writeLn ('Вторник');
3 : writeLn ('Среда');
else
writeLn ('Какой-то другой день.');
end;

Итак ключ выбора - это у нас day. Для него мы составляем список выбора - список возможных значений и что мы будем делать при этих значениях. Так если day = 1, то мы выведем Понедельник, если 2 - Вторник и т.д. Часть, которая начинается с else может отсутствовать - она схожа с частью оператора if-then-else. Поэтому она выполняется, если ключ выбора не удовлетворяет ни одному условию из списка. У нас она выполнится, если day не равен 1, 2 или 3, например при day=0 или day=4.

О том, что никогда не меняется - константы! Константы - это переменные, которые не могут изменять своего значения (или переменные - это константы, которые его меняют smile Так вот константы - это очень важная часть программы, для их описания выделена целая сексия. Константы имеют имя и значение. Секция констант начинается со слова const. Эта секция обычно распологается перед секцией переменных. Теперь шаблон у нас превращается в нечто более сложное:

Program ИмяПрограммы;
uses список подключаемых модулей;
const
Здесь идёт объявление констант.
var
Здесь идёт объявление переменных.
begin
Здесь идёт текст программы.
end.

Объявить константу можно следующим образом: ИМЯ_КОНСТАНТЫ = ЗНАЧЕНИЕ; Обратите внимание, что мы пишем просто равно, без двоеточия! Например напишем программу, которая считает число П/2 (П = 3.14259265)

Program PI_DEL_TWO;
const
pi = 3.14159265;
var
pid2 : real;
begin
pid2 := pi / 2
end.

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

const
Text = 'Hello, World!';
begin
writeLn (Text)
end.

- ещё одна вариация на тему hello world! Зачем нужны константы, ведь вполне можно использовать численные значения ? Константы нужны для удобства! При компиляции они автоматически заменяются своими значениями. Но например, если это число нужно изменить ? (число П не изменишь, но например стоимость товара или ширина шкафа) Тогда просто меняете значение константы, номер версии программы на 1.01 и получаете совершенно новый продукт smile
Программа

Давайте сегодня напишем маленькую ходилку - нажимая на клавиши будем двигать курсор по экрану. Управление выберем самое примитивное.... нет не стрелки smile ... они как не странно несколько сложнее, а буквы w, a, s, d. Выход из программы будем осуществлять по клавише ESC. Для передвижения курсора нам понадобится функция gotoxy (X, Y) - передвижение курсора на координаты X,Y. При этом левому верхнему углу экрана соответсивуют координаты (1,1) а правому нижнему (80, 25). Это функция из модуля CRT.

Program CURSOR;

uses CRT;

const
LEFT = 'a';
RIGHT = 'd';
UP = 'w';
DOWN = 's';
ESC = 27;

var
x, y : integer;
key : char;

begin
ClrScr;

key := #0;
x := 1;
y := 1;

while key <> chr (ESC) do
begin
key := readkey;

case key of
LEFT : begin
if (x - 1) >= 1 then
x := x - 1
else
x := 80
end;
RIGHT : begin
if (x + 1) <= 80 then
x := x + 1
else
x := 1
end;
UP : begin
if (y - 1) >= 1 then
y := y - 1
else
y := 25
end;
DOWN : begin
if (y + 1) <= 25 then
y := y + 1
else
y := 1
end
end;

gotoxy (x, y)
end
end.

Что сразу бросатеся в глаза ? Наверное key := #0; - что это за #0 ? Значек диез "#" означает, что это не число, а символ! Существует так называемая таблица символов, каждый символ имеет в ней свой номер. Например весёлая рожица - #1, цифра 0 - #48, буква Ъ - #154, буква ъ - #234. Заглавные и строчные буквы имеют разные номера. Символы можно выводить с помощью функции write, например write (#1, #32, #2) - посмотрите, что выйдет (#32 - символ пробела). Вот мы и присваиваем переменной key символ под номером 0! key - переменная типа char (анг. символ). Переменные типа char имеют размер в 1 байт и принемают значения от 0 до 255. В переменных x и y - у нас хранятся координаты. В начале мы присваем им значения 1,1 - т.к. после выполнения Clrscr курсор перемещается в эту точку. Цикл while у нас идёт с условием key <> chr (ESC). Функция chr (X) преобразует целое число к символу. Константа ESC имеет значение 27 - код клавиши ESC, однако мы помним, что Паскаль - строго типизированный язык, поэтому мы должны преобразовать целое число 27 к символу 27! key := readkey; - функция readkey считывает символ с клавиатуры, не выводя его на экран и возращает его. Теперь вырисовывается алгоритм smile программы - считываем клавишу с клавиатуры, проверяем его на совпадение с функциональными клавишами. Цикл выполняется, пока мы не нажмём клавишу ESC. Эта программа представляет собой упрощение алгоритма лежашее в основе многих игрушек.


 

semen
Дата: Суббота, 30.10.2010, 17:33 | Сообщение #9
Администраторы
Сообщений: 301
Репутация: 150
Помните выпуск про циклы? Конечно помните! Так вот если попробовали сотавить парочку программ самостоятельно (боже, как я на это надеюсь!) у вас могла возникнуть необходимость выйдти из цикла. Как этого добится я не упомянул. Пришло время закрыть это белое пятно. Итак для управления циклами есть 2 функции. break и continue. Судя по названиям они служат для прерывания и продолжения циклов. BREAK (англ. сломать, испортить) - немедленно прекращает выполнение цикла, переходя на следующий за циклом оператор. CONTINUE (англ. продолжение) - обеспечивает продолжение цикла. Давайте рассмотрим примеры:

j := 0;
for i := 0 to 10 do
begin
j := j + 1;
if j = 5 then break
end;

Этот цикл прервётся при j = 5, несмотря на то что i в этот момент равно 4!

j := 0;
i := 0;

while i <> 10 do
begin
j := j + 1;
if j > 5 then
continue;
i := i + 1
end;

При выходе из цикла в i будет 10, а j =-32764. Почему так? Просто при j >= 5 происходит продолжение цикла, т.е. i := i + 1 не выполняется. Управление передаётся на while i <> 10 do. Но когда j = 32767 прибавляя 1 мы получаем j < 5 и тогда цикл продолжает выполняться нормально. Однако существует второй способ выхода из цикла. Это оператор безусловного перехода. Его смысл заключается в переходе на определённую строчку программы. Записывается он в виде:

goto Метка;

Метки объявляются в отдельном разделе программы под названием label. И шаблон программы превращается во всё более сложное творение:

Program ИмяПрограммы;
uses список подключаемых модулей;
const
Здесь идёт объявление констант.
label здесь объявляются метки.
var
Здесь идёт объявление переменных.
begin
Здесь идёт текст программы.
end.

Пометить строчку, т.е. поставить на неё метку, можно так:

label normal, bad;
.....
......
goto normal;
......
normal : writeLn ('All Ok!');
.......
bad:
writeLn ('All bad!');
.....
......
goto bad;
.......

Итак как вы видите мы просто ставим на строке имя метки и двоеточие. Когда программа натыкается на goto она сразу же переходит на метку, несмотря ни на что! Сразу хочу сказать, что использование goto является плохим тоном. Программа должна быть легко читаемой, а при goto происходят постоянные прыжки по тексту программы. Если вы используете Бейсик, то забудьте об этом !!! Поэтому мы не будем использовать goto в своих программах, ну разве только сегодня для примера.
Программа

Program GotoTest;
uses CRT;
label start;

var
key : char;
i : integer;

begin
start:
for i := 0 to 20 do
writeLn ('Do something...');
write ('Ещё раз? [Y/N] ');
key := readkey;
writeLn (key);
if key = 'Y' then
goto start
else
if key = 'y' then
goto start;
WriteLn ('Thanks for using our product [c] 2002')
end.

Сия простая программка демонстрирует принцип построения программы, которая делает что-то, а потом спрашивает надо ли это повторить. Обратите внимание, что когда мы проверям какую клавишу нажал юзер, мы проверяем на клавишу 'Y' и 'y' . Эти клавиши имеют раные коды, поэтому мы должны проверить оба варианта! Естественно этот вариант не является единственным. Мы могли сделать цикл while 1 do ... и потом просто проверяя клавишу на совпадение использовать break. Вот так безо всяких вредных goto!


 

semen
Дата: Суббота, 30.10.2010, 17:33 | Сообщение #10
Администраторы
Сообщений: 301
Репутация: 150
Давайте представим, что нам необходимо создать картинку в памяти. Если описывать каждую точку координатами (x, y) , то понадобится очень много переменных. Однако задачу как всегда можно упростить. В этом нам помогут массивы! Массив - объединение нескольких однотипных объектов. Массивы объявляются в разделе переменных (var) вот так:

ИМЯ : array [ диапозон ] of ТИП;

Диапозон определяет число элементов массива, а тип - тип элементов массива. Например:

A : array [0 .. 50] of real;
B : array [ -3 .. 4] of integer;
С : array [1 .. 10] of byte;

Как видно в квадратных скобках указывается минимальный и максимальный номер элемента массива. В примере массив А содержит 51 элемент, массив В - 8 элементов, С - 10. К каждому элементу массива осуществляется отдельный доступ по его индексу (он колеблется в диапозоне, например для В это от -3 до 4, для А от 0 до 50). Индекс элемента указывается в квадратных скобках :

a [0] := 5; - присвоить первому элементу массива А значение 5
a[1] := a[0]; - присвоить второму элементу массива А значение первого
b[-3] := b[4]; - присвоить первому элементу массива В значение последнего
c[5] := 5; присвоить 5 элементу массива С значение 5

Из этого примера видно, что номер элемента массива соответствует индексу при способе задания массива как-то так array [1 .. С элементом массива допустимы любые операции (естественно, если они допустимы для типа). Т.е. вы спокойно обращаетесь с элементом массива как с переменной. Давайте рассмотрим простенькую программку - заполняем массив случайными числами и выводим его на экран.

Program Test;

uses CRT;

const N = 10;

var
A : array [1..N] of integer;
i : integer;
begin
ClrScr;
randomize;

for i := 1 to N do
begin
A[i] := random (256);
writeLn ('A[', i, '] = ', A[i])
end
end.

В качестве верхней границы массива мы везде используем константу, что очень удобно (если на понадобится массив скажем от 1 до 100, то мы просто её поменяем). После очистки экрана мы вызываем функцию randomize - инициализация генератора случайных чисел. После этого мы можем использовать функцию random (X), которая возвращает случайное число в границах [0, X), т.е. больше или равно 0 и меньше Х. Про то, как этот генератор работает, вернее откуда они берутся эти случайные числа, если в компьютере всё построено на чётких и явных командах?
Программа

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

B=20,-5,10,8,7, исходный список.
B1=-5,10,8,7,20, первый просмотр. (Поменяли -5 и 20, потом 20 и 10, потом 20 и 8, потом 20 и 7)
B2=-5,8,7,10,20, второй просмотр. (Протащили 10.)
B3=-5,7,8,10,20, третий просмотр. (Протащили 8, остальные элементы встали на свои места.)

цитата iVs 02.11.2002


 

semen
Дата: Суббота, 30.10.2010, 17:34 | Сообщение #11
Администраторы
Сообщений: 301
Репутация: 150
Давайте подумаем, как мы можем представить строки ? Нам известен тип символов (char) поэтому строку вполне логично представить, как массив символов. Вот и в Паскале есть тип String, который очень похож на array [0 .. N] of char. N можно задать явно (объявить переменную String[N]) или вообще не задавать, тогда будет использоваться значение 255. К любому символу строки можно обратится точно так же как и к элементу массива. Давайте рассмотрим примерчик:

var
st : String;
begin
st := 'Turbo Pascal';
.....

Самый первый элемент строки st [0] - содержит длинну строки. Дальше идут символы строки. Для нашего примера st[0] = 12, st[1] = 'T', st[2] = 'u' и т.д. Длинну строки (в целых числах) можно получить двумя способами:

1. функция LENGHT (STR) - возвращает длинну строки str
2. Использовать ORD (st[0])

Например так:

var
st : String;
i : integer;
....................
i := ord (st[0]); - в i длинна строки
.....................
i := lenght (st ); - в i длинна строки
...........

Если длинна строки будет больше N, то строка автоматически урежется до нужного размера! Например программа

var
st : String [10];
begin
st := 'Turbo Pascal';
writeLn (st)
end.

выведет на экран Turbo Pasc Вот так! Будте внимательны! К строкам можно применять операцию "сцепление" - "+". Например st := 'a' +'b', потом st = 'ab' для работы со строками в Паскале имеется мощный арсенал функций:

* CONCAT (S1, S2, ... , Sn) - получает переменное число параметров (как write). Возвращает строку, представляющую собой сцепление строк S1, S2, ..Sn.
* COPY (ST, INDEX, COUNT) - возвращает строку, содержащую COUNT символов строки ST, начиная с номера INDEX
* DELETE (ST, INDEX, COUNT) - удаляет COUNT символов из строки ST, начиная с символа под номером INDEX.
* INSERT (SUB, ST, INDEX) - вставляет строку SUB в строку ST, начиная с номера INDEX.
* LENGHT (ST) возвращает целое число (integer) - длинну строки ST
* POS (SUB, ST) - ищет строку SUB в строке ST (до первого нахождения) . Возвращает номер позиции с которой она начинается, если строка не найдена, то 0. Тип возращаемого значения - Integer
* STR (X, ST) - преобразует число Х (любого типа) в строку символов ST (из 123 получим '123'). Число Х может иметь формат. Подробнее об формате смотрите выпуск "Типы данных".
* VAL (ST, X, code) - перобразует строку ST в целую или вещественную переменную Х. code содержит 0, если преобразование прошло успешно. В противном случае - номер ошибочного символа. Например val (' 123', x, c) будет x = 123, с = 0. НО val ('123 ', x, c) - с = 4. Т.е. пробелы допустимы перед числом, но не после него!

Конечно это краткое описание не может дать полног представления об функциях, но увы времени на подготовку этого выпуска совсем мало :(((( Поэтому наша фирма предоставляет вам уникальную, доселе невиданную возможность smile - поэксперементировать с этими функциями. Напишите программку, которая просто по очереди вызывает функции для работы со строками. Так же над строкаи можно выполнять операции сравнения (=, <> , >, <, >=, <=). Но подробнее об этом в разделе программа.
Программа

Сегодня мы напишем программу, которая выведет на экран таблицу символов. В этой таблице 256 символов. С некоторыми символами вы уже знакомы - это буквы. Но есть ещё большое количество символов - так называемые символы псевдографики. С их помощью можно например чертить таблицы. Весь интерфейс BP организован с помощью этих символов. Вот сегодня мы и выведем их на экран! Для этой цели можно было бы использовать array [1..256] of char, но куда проще написать это через строку!

Program Table;

var
st : string;
i : integer;
begin
for i := 0 to 255 do
begin
st [i] := chr (i);
write(st[i], #32)
end
end.

Итак как вы видите мы использовали строку просто как массив, если вы попытаетесь вывести её на экран (write (st)), то ничего не выйдет. Почему ? Потому что мы в цикле присваеваем всем символам строки номер. А st[0] = 0 !!! А ведь в st[0] у нас хранится длинна строки ! Я надеюсь, что все уже успели выучить, что Паскаль - строго типизированный язык. Поэтому что бы присвоить символу целое значение, нам нужно его преобразовать к символу. Это и делает функция chr ! Теперь мы познакомились с таблицей символов. Обратите внимание, что руские буквы идут не подряд. Вначале от 'а' до 'п' потом 'р' до 'я' и ещё в конце идёт буква 'ё' ! Вернемся к сравнению строк. Все операции сравнения над двумя строками выполняются по-символьно слева на право, с учётом таблицы символов. Если одна строка меньше другой, то короткая дополняется 0-ым символом до нужной длинны! Например следующие сравнения будут истинны:

'A' > '1'
'a' > 'A'
'Turbo' < 'Turbo Pascal'
'Паскаль' > 'Pascal'


 

semen
Дата: Суббота, 30.10.2010, 17:34 | Сообщение #12
Администраторы
Сообщений: 301
Репутация: 150
Сегодня перед вами откроется страшная тайна.... всё это время мы вас беспощадно обманывали !!!!!!!!! smile
На самом деле всё не так плохо, просто решили, что не стоит сначала загружать народ не понятными терминами. Сегодня всё встанет на свои законные места. Помните функцию ClrScr - так вот тайна её рождения состоит в том, что это ПРОЦЕДУРА. Процедуры и функции очень похожи друг на друга. Основное отличие состоит в том, что функция может нам что-то возвратить, а процедура нет. ClrScr - ничего нам не возвращает => процедура, Round - возвращает => функция. В некоторых языках (самый яркий пример - С/С++) вообще нет понятия процедуры - там только функции. При этом в качестве процедур используются функции которые ничего не возвращают. В Паскале же процедуры и функции разделили между собой. Итак давайте научимся писать свои собственные процедуры и функции. Описание процедуры начинается со следующего заголовка:

procedure ИМЯ ( СПИСОК_ПАРАМЕТРОВ );

Список параметров может отсутствовать (например всё та же ClrScr - нет параметров). В остальном процедура очень похожа на программу. Кстате процедуры и функции вместе называют - под-программы! Сегодня мы поговорим о процедурах. Плюсы в использовании процедур очевидны - не надо много раз повторять один и тот же код - достаточно просто указать имя процедуры. Давайте напишем такой пример:

procedure DisplayString;
var
i : integer;
begin
ClrScr;
for i := 0 to 10 do
writeLn ('Write something ....' )
end;

Как видите у процедуры есть своя секция переменных, секция меток, констант и секция выполняемых инструкций (секция кода). Код процедуры начинается соответственно со слова begin и кончается словом end. Обратите внимание, что после end мы поставили точку с запятой! Как вы помните точка - это признак конца программы, а программа не заканчивается процедурой! Можно сказать, что до этого наша программа и была одной процедурой. Только без имени. Например в том же С - у этой фунции есть имя main. Процедуры являются под-программами, поэтому они могут в свою очередь содержать под-под-программы, которые тоже могут содержать под-под-под-программы smile и так далее. Например структура нашей программы выглядела так (слева для наглядности нарисована схема, а справа написан текст программы):

Program ...;

begin
........
end.

Если мы добавим две под программы, то она изменится в такую:

Под-программа А

Program ...;

procedure A;
begin
.......
end;

Под-программа B

procedure B;
begin
........
end;

begin
........
end.

Если мы к под-программе А добавим две под-программы, то структура изменится в следующую строну:

ПРОГРАММА

Program ...;

begin
........
end.

Под-программа А

procedure A;

begin
.......
end;

под-программа А1

procedure A1;
begin
........
end;

под-программа А2

procedure A2;
begin
..........
end;

Под-программа B

procedure B;
begin
........
end;

Program Day_of_week;

uses CRT;

procedure GetDay (var d, m, y : integer);
var
correct : byte;
begin
correct := 1;
repeat
writeLn ('Введите дату.');
write ('День - ');
readLn (d);
write ('Месяц - ');
readLn (m);
write ('Год - ');
readLn (y);

{ Проверим правильность года. Если год не "правильный", то попросим ввести дату ещё раз }
if y <= 4903 then
if y >= 1582 then
correct := 0;
until correct = 0
end;

procedure CountDay (d, m, y : integer);
var
week : array [0 .. 6] of string[11];
c, w : integer;
begin
week [0] := 'воскресенье';
week [1] := 'понедельник';
week [2] := 'вторник';
week [3] := 'среда';
week [4] := 'четверг';
week [5] := 'пятница';
week [6] := 'суббота';

{ Месяц январь или февраль = > нужно преобразовать }
if m < 3 then
begin
m := m + 10;
y := y - 1
end
else
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 ('соответствует день недели - ', week [w])
end;

var
d, y, m : integer;

begin
ClrScr;
GetDay (d, m, y);
CountDay (d, m, y)
end.

Программа работает по принципу "спросил-ответил". Пользователь вводит дату, программа выдаёт день недели. Думаю вам понятно как построить на основе этих данных "вечный" календарь. Комментировать особенно нечего, кроме того, что я пренебрёг собственными рекомендациями и назвал параметры в процедурах и глобальные переменные одинаково smile Обратите внимание на следующее - в процедуре получения даты (GetDay) мы передаём адресса переменных => при выходе из процедуры их значения изменятся. А в процедуре вычисления дня недели (CountDay) мы передаём просто значения переменных => после выхода из процедуры они не изменятся. Неплохо бы исселдовать программу в отладчике. До этого мы для пошагового исполнения использовали клавишу F8. Однако что бы заходить в функции и процедуры нам подребуется нажимать клавишу F7. Используя F8 (пошагово без захода в функции и процедуры) мы увидим исполнение 3-х строчек. А что б увидеть исполнение всех процедур и функций программы нам нужно использовать пошаговый режим с заходом в функции F7. Ещё можно обратить внимание на объявление массива строк. String это такой же тип как и integer поэтому мы вполне можем объявить массив типа String. Так же можно добавить проверку на корректность введённого дня и месяца.


 

semen
Дата: Суббота, 30.10.2010, 17:35 | Сообщение #13
Администраторы
Сообщений: 301
Репутация: 150
Как уже отмечалось ранее функции очень похожи на процедуры. Объявить функцию можно следующим образом:

function ИМЯ ( СПИСОК_ПАРАМЕТРОВ) : ТИП_ВОЗВРАЩАЕМОГО_ЗНАЧЕНЯ

Например в объявим функцию для возведения числа в степень. Параметры (число и степень) и возвращаемое значение - будут типа Real.

function Power (a, b : real) : real;

Однако нам нужно вернуть какое-то значение. Что бы особо не загружать язык сделали очень просто: для хранения возвращаемого значения используется переменная по имени совпадающая с именем функции. Например:

function F : integer;
begin
F := 3
end;

такая функция всегда возвратит 3. Давайте теперь договоримся об обозначениях. Раньше мы писали просто имя процедуры (или функции), когда рассказывали о ней. Теперь же мы будем кроме описания помещать прототип функции (процедуры) - её заголовок. Например в рассказе об ClrScr мы бы описали её так:

procedure Clrscr;

а функцию Round мы опишем так:

function Round(X: Real): Longint;

таким образом мы получаем полное представление о параметрах и возвращаемом значении. Конечно это не отменяет словесного описания функции. Думаю вы догадались, что для функций справедливо всё сказанное про процедуры. Поэтому не надо повторятся про вложенность под-программ, объявление переменных, области видимости и тому подобные вещи. Вместо этого напишем пример илюстрирующий применение функции. Например в Паскале нет стандартной функции для возведения в степень. Часто это отанавливает математически неподкованных людей. Будем надеятся, что с логарифмами и экспонентами все знакомы. Поэтому с помощью 2-х встроенных функций (для вычисления логарифма и экспаненты) можно реализовать несложный алгоритм. Но сначала пара слов о самих функциях:

function Ln(X: Real): Real;

возвращает значение натурального логарифма числа Х.

function Exp(X: Real): Real;

возвращает значение числа е, возведённого в степень Х. А теперь сама функция:

function power (a, b : real) : real;
begin
if a > 0 then
power := exp (b * ln (a))
else
if a < 0 then
power := exp (b * ln (abs (a)))
else
power := 0
end;

данная функция рассматривает сразу три случая: а > 0, a < 0 и a = 0. Однако сразу напрашивается четвёртый вариант: когда b=0. Ведь любое число в 0 степени - это 1. Поэтому зачем нам тратить лишнее время на вычисление логарифмов и экспаненты ? Давайте сразу проверим b и присвоим power значение 1. Добавим такой код в начало функции:

if b = 0 then
power := 1

Давайте посмотрим в отладчике как происходит выполнение нашей функции (при b=0) жмите F7.... и наблюдайте, что всё равно происходит проверка условий и подсчёт экспанент... м-да. Однако выход (в буквальном смысле этого слова smile есть всегда - процедура exit (англ. выход):

procedure exit;

она завершает выполнение текущей под-программы(функции, процедуры) или программы (если она идёт в теле программы). Вот мы и добавим вызов этой процедуры:

if b = 0 then
begin
power := 1;
exit
end;

Давайте посмотрим работу exit через отладчик ... вроде всё работает, НО теперь если a = 0 и b = 0, то возвращаемое значение будет 1, хотя нужно возвращать 0 (это ошибка, математики не определились про 00). Так что нужно проверить и этот случай. Думаю, что довести до ума функцию для вас не составит труда.
Домашнее задание

Итак как вы помните мы решили провести эксперимент с домашним заданием: ваши мысли по поводу работы генератора случайных чисел и сортировка массива. Про сортировку мы поговорим в разделе программа. Сейчас место генератора. Всего было полученно 7 ответов. Все они были правильные. Некоторые из них содержали просто 3 слова по этому поводу, некоторые из них довольно обширные. Как же на самом деле работает генератор случайных чисел ? Итак в компьютере всё постоянно, ничего не меняется.... только если.... ага! посмотрите в правый нижний угол экрана smile правильно ЧАСЫ! Вот что меняется ! Итак ваша догадка совершенно правильна. В основе случайных чисел лежит таймер. Однако само время не случайно по своей природе. Поэтому на основе одного момента времени составляется последовательность чисел, которые потом выдаются. Поэтому мы и дожны инициализировать генератор случайных чисел с помощью процедуры randomize! Сама же теория получения случайных чисел была изложена в одном из ответов, который мы и процитируем:

цитата Ivs 10.11.2002

Для работы ЭВМ со случайными числами вначале пытались вводить эти числа из вне. Вводили в память готовые таблицы случайных чисел. Строили приборы, использующие случайные физические процессы, например радиоактивный распад, и вводящие полученные числа в машину. Все это было достаточно плохо.Таблицу случайных чисел ЭВМ быстро исчерпывала, а случайное физическое явление нельзя было повторить, чтобы проверить вычисления. Таким образом возникла пародаксальная задача - вырабатывать в самой ЭВМ случайные числа, но такие, чтобы их можно было повторить и чтобы последующее число вычислялось по предыдущим, но не зависело от них. Задача, разумеется, неразрешимая. Но Дж.Нейман придумал алгоритм, последовательно вычисляющий числа x1,x2,..., очень похожие на случайные, равномерно распределенные от 0 до 1. Их называют псевдослучайными, или просто случайными. Этот алгоритм такой: Метод середины квадрата. Будем строить 4-х значные псевдослучайные числа. Возьмем произвольное целое k из диапазона 0<k<10^4. Это будет наше первое число. Если нам уже известно k(i), то возведем его в квадрати возьмем 4 средних разряда. Если, например, k(i)=2251, то возведя в квадрат, получим k(i)^2=05067001, и поэтому k(i+1)=0670. Чтобы получить из нашей последовательности k(1),k(2),...последовательность действительных чисел x1,x2,... равномерно распределённые между 0 и 1, надо положить: x(i)=k(i)/10^4. Несмотря на очевидные возражения, вроде того, что эта последовательность непременно начнет повторяться или что она может выродиться в сплошные нули, числа x1,x2 похожи на случайные. В среднем в половине случаев x(i)<x(i+1), а в половине наоборот. Среди первой тысячи чисел x1,x2,...непременно половина будет меньше 1/2, а половина больше, и тд. Разработаны и более сложные способы получения псевдослучайных последовательностей, препятствующие обращения членов последовательности в нули и удлиняющие период её повторения. Но принцип они используют тот же, и для понимания существа дела достаточно изложенного.

Программа

Вернёмся к программе сортировки массива. Итак задача стояла следующим образом (краткое содержание предыдущих 5000 серий smile - программу, которая сортирует массив пузырьковой сортировкой. Этим делом мы сегодня и займёмся. Естественно, что мы используем полученные знания про процедуры и функции. При этом мы будем считать число сравнений и перестановок. Поэтому напишем такую простую функцию для сравнения двух чисел. При этом переменная CmpCount (число сравнений) предпологается глобальной:

function cmp (a, b : integer) : integer;
begin
CmpCount := CmpCount + 1;
cmp := ord (a > b)
end;

Я надеюсь вы помните, что ord преобразовывает логическое выражение к целому числу. У нас логическое выражени a > b истинно, когда а больше b, поэтому функция вернёт нам 1 в этом случае и 0 в случае, если а меньше b. Так же напишем процедуру для перестановки значений a и b. В этом случае воспользуемся адрессами переменных. Переменная SwapCount - число перестановок - глобальная:

procedure swap (var a, b : integer);
var
c : integer;
begin
c := a;
a := b;
b := c;
SwapCount := SwapCount + 1
end;

Здесь я думаю всё прозрачно. Теперь подойдём к реализации алгоритма сортировки. Основная мысль алгоритма: сравниваются два соседних элемента, если порядок нарушен, то они меняются местами. Первая мысль, которая приходит ко мне в голову: нужно продолжать поиск по всем элементам до тех пор пока будет совершена хоть одна перестановка. Давайте введём переменную flag - когда она равна 1, то совершенна перестановка, если 0 - то перестановки не было. Поэтому напишем такое начало:

const
Size = 10;
var
flag : integer;
i : integer;
CmpCount : integer;
SawpCount : integer;
a : array [1 .. size] of integer;

procedure PrintArray;
var
i : integer;
begin
for i := 1 to Size do
write (A[i] : 4);
writeLn
end;

begin
randomize;
flag := 1;
SwapCount := 0;
CmpCount := 0;

for i := 1 to Size do
A[i] := random (100);

PrintArray;

{Начало сортировки}
while flag = 1 do
begin
flag := 0;
end;

PrintArray;
writeLn ('Число сравнений - ', CmpCount);
writeLn ('Число перестановок - ', SwapCount);
ReadLn
end.

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

while flag = 1 do
begin
flag := 0;
for i := 1 to Size - 1 do
if cmp (A[i], A[i +1]) = 1 then
begin
swap (A[i], A[i + 1]) ;
flag := 1
end
end;

обратите внимание, что в цикле мы идём до Size - 1 так как сравниваем i-тый элемент с i+1 элементом. Поэтому i+1 не должно выходить за границу массива, которая у нас и равна Size (т.е. i + 1 <= Size следовательно i <= Size - 1). Мы устанавливаем flag в 1, только если произошла перестановка! Таким образом число сравнений и обменов у нас получается: (Size - 1) * Size За один проход максимальный элемент становится на свою позицию, по этому последний элемент проверять не надо. За второй проход не надо проверять предпоследний и так далее. Поэтому мы можем запомнить максимальный номер соседа, с которым происходило сравнение. И пробегать в цикле соответственно до этого номера

n := Size;

while n > 0 do
begin
last := 0;
for i := 1 to n - 1 do
if cmp (B[i], B[i + 1]) = 1 then
begin
swap (B[i], B[i + 1]);
last := i + 1
end;
n := last
end;

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


 

semen
Дата: Суббота, 30.10.2010, 17:35 | Сообщение #14
Администраторы
Сообщений: 301
Репутация: 150
Итак сегодня мы узнаем ещё один тип данных - логический. С ним вы вообщем-то уже знакомы. Однако всё по порядку. Имя у этого типа - boolean. Переменная может принемать только два значения - true (истина) и false (ложь). Помните сколько будет 2*2=4 ? будет trUE ! При этом для этих значений справедливо следующее:

ord (false) = 0
ord (true) = 1
false < true

вот и выходит, что правда (то биш истина) сильнее smile В Паскале определены следующие логические операции:

* not - логическое НЕ
* and - логическое И
* or -логическое ИЛИ
* xor - исключающее ИЛИ

страшные имена smile синтаксис этих операций таков, кроме not:

операнд1 ОПЕРАЦИЯ операнд2

Для not:

not операнд

Эти операции применяются к целым и логическим переменным. Результат соответсвенно тоже целый или логический. Вначале рассмотрим целые числа. Помните двоичную систему счисления... так вот биты результата формируются по следующим правилам:
AND OR XOR NOT
операнд 1 0101 0101 0101 0101
операнд 2 0011 0011 0011
Результат: 0001 0111 0110 1010
Для простоты мы предположили, что в числе у нас 4 бита (4 двоичных разряда). А теперь те же правила, но в текстовом варианте:

* AND - если оба сравниваемых бита раны 1, то результат равен 1, в остальных случаях 0
* OR - если хотя бы один из сравниваемых битов равен 1, то результат равен 1, если оба 0, то результат - 0
* XOR - если один из сравниваемых битов равен 0, а другой еденице, то результат равен 1, в других случаях (оба 0 или оба 1) результат - 0
* NOT - устанавливает обратное значение битов: 0 становится 1, а 1 - 0.

Вот такие дела! Рассмотрим вечный пример:

var
a, b : integer;
begin
a := 31;
b := a xor a;
writeLn (b)
end.

Что же выведет такая программа ? думаем.... думаем ... смотрим на описание xor .... думаем ... думаем ..... придумали! А ведь она выведет 0 !!! Почему ? Да потому, что xor даёт 0, если оба сравниваемых бита равны! А в числах а и а биты равны smile Тепрь для логических переменных
AND OR XOR NOT
операнд 1 false true false true false true false true false true false true false true
операнд 2 false false true true false false true true false false true true
Результат: false false false true false true true true false true true false true false
Таблица получилась конечно запутанная, но смысл тот же - если заменить true на 1, а false на 0. Так вот к чему мы всё это... при проверке условия (например в if или while) мы можем проверить сразу несколько условий. Например, следующее выражение будет истинно при а = b и c = d:

(a = b) and (c = d)

а что нам мешает сделать так:

if (a = b) and (c = d) then
это выполняется когда оба условия истинны, т.е. a=b и c=d одновременно!
else
это выполняется, когда хотя бы одно из условий ложно.

или так:

if (a = b) or (c = d) then
это выполняется когда хотя бы одно из условий истинно, т.е. a=b или (и) c=d
else
это выполняется, когда оба условия ложны.

или в конце-концов так:

if (a = b) xor (c = d) then
это выполняется когда одно условие истинно, а второе ложно, т.е. a=b или c=d
else
это выполняется, когда оба условия истинны или оба ложны.

Конечно можно соединять и пять и десять условий... главное не попутайтесь, раставляя скобки!
Программа
Сегодня мы напишем программу, которая преобразует строку строчных букв к строке с заглавными буквами. Данный алгоритм будет работать только для латиницы! Итак мы помним (в чём я сомваюсь :)) что английские буквы распологаются в таблице символов следующим образом: заглавные буквы (A-Z) имеют коды 41h - 5Ah, строчные (a-z) соответственно 61h-7A. Поэтому нам сначала нужно прозаботится о проверке попадания буквы в нужный диапазон. Давайте рассмотрим двоичное представление чисел 41h (буква А) и 61h (буква а).
Биты 76543210
Буква А: 01000001
Баква а: 01100001
Думается вы заметили, что разница в 5-ом бите. Пэтому нам просто нужно его изменить и сделать из 1 равным 0. Каким образом? Давайте посморим на таблицу .... смотрим .... смотрим ..... ещё раз .... ещё два .... хватит. Что предложите использовать ? Я лично and, где вторым операндом будет страннное число 11011111b .... давайте убедимся, что всё произойдёт так как я и обещал:AND
Буква а: 01100001
11011111
Буква А: 01000001
убедились ? Я да!

program up;

var
i : integer;
str : string;
begin
str := 'I''m world-best Pascal programmer :)';
writeLn (str);

for i := 1 to ord (str[0]) do
begin
if (str[i] >= 'a') and (str[i] <= 'z') then
str[i] := chr ( ord(str[i]) and 223)
end;

writeLn (str)
end.

обратите внимание на эту конструкцию: chr ( ord(str[i]) and 223) - сначала мы преобразовываем символ str[i] к целому числу (функция ord), потом выполняем and, потом преобразуем целое число к символу (функция chr).


 

semen
Дата: Суббота, 30.10.2010, 17:36 | Сообщение #15
Администраторы
Сообщений: 301
Репутация: 150
Единственное, что я хочу сказать перед практикой, так это то, что предложенные решения не являются единственными. Итак в прошлый раз я не объяснил про полиндром. Вообщем это довольно простая програмка. Хотя, когда сталкиваешься с этим в первый раз, всё таки задумываешься. Вообщем основная мысль такая: мы пробегаем строку с начала и с конца одновременно, пока не встретимся. Если при этом символы, отстояшщие на равном растоянии от концов, не будут равны , значит слово не полиндром: К сожалению в оригинальном выпуске в программе была допущенна ошибка. Правильный вариант программы приведён ниже:

Program Polindrom;

var
str : string;
i, j : integer;
flag : boolean;
begin
i := 1;
flag := true;

readLn (str);
j := ord (str[0]);

while i < j do
begin
if str[i] <> str[j] then
begin
flag := false;
break
end;
{ ВОТ ЭТО БЫЛО ПРОПУЩЕННО !! }
i := i + 1;
j := j - 1
end;

if flag then
writeLn ('ДА !')
else
writeLn ('НЕТ !')
end.

Спасибо Александру, который указал на эту неточность. Следующее задание, которое я не рассмотрел, посвященно переводу строки из заглавных букв в строчные. Давайте вспомним, что нам нужно сделать, для этого.
Биты 76543210
Буква А: 01000001
Баква а: 01100001
значит нам для преобразования A в а нам нужно поменять 5 бит с 0 на 1. Остальные биты нужно не трогать. Давайте вспомним логические операции. Например OR - 0 or 1 = 1 или xor - 0 xor 1 = 1. Обе операции подходят. Теперь давайте придумаем число. Очевидно, что оно будет выглядеть как-то так:
Биты 76543210
**1*****
При этом биты, обозначенные *, не должны поменятся. Давайте подберём число, что бы * or 1 = 1 и * or 0 = 0. Очевидно, что это число 0. Кстати эти рассуждения подойдут и для операции xor. Поэтому число у нас превращается 00100000b. Ну и соответственно текст программы:

program down;

var
i : integer;
str : string;
begin
str := 'i''M WORLD-BEST pASCAL PROGRAMMER :)';
writeLn (str);

for i := 1 to ord (str[0]) do
begin
if (str[i] >= 'A') and (str[i] <= 'Z') then
str[i] := chr ( ord(str[i]) or 32)
end;

writeLn (str)
end.

Теперь обратимся в сторону математики. Например займёмся тригонометрией. Стандартные функции языка такие:

function Sin(X: Real): Real; - возвращает sin угла х (в радианах).
function Cos(X: Real): Real; - возвращает cos угла х (в радианах).
function ArcTan(X: Real): Real; - возвращает arctg числа Х.

Если вы не владеете терминологией, то я скажу лишь кое-что. Остальное предоставим предподавателям математики. С sin и cos я предпологаю знакомы все. Итак тангенсом угла А называется выражение tg A = sin A / cos A Котангенсом называется выражение: ctg A = 1 / tg A = cos A / sin A Соответственно существуют обратные функции, которые по значению дадут нам угол. Эти функции имеют те же имена, но с приставкой arc (arccos, arcsin, arctg, arcctg). Многие ошибочно пологают, что раз называется обратная функция, то она равна 1 / функцию. Это не так. Давайте рассмотрим пример: пусть у нас есть два множества А и В:
множество А

множество В

пускай у нас есть функция B = F (A) т.е. некое правило, которое ставит в соответствие любому элементу множества А некий элемент множества В.
множество А
-> F (A) ->
множество В

Обратная функция (правило) должно нас возвращать из В в А. т.е. нужно придумать такую функцию (A = G (B)), что бы каждому элементу множества В ставился элемент А
множество А
-> F (A) ->
<- G (B) <-
множество В

Поэтому arcsin не равен 1 / sin. Надеюсь, что это понятно. Теперь посмотрим на наш скудный арсенал функций - всего 3 штуки.... мда. не густо. Но ведь язык Паскаль и был создан для обучения программированию. Вот сейчас мы и научимся считать любые тригонометрические функции. Думаю вы уже догадались, как можно сосчитать tg и ctg, если не догадались, то посмотрите ещё раз сюда. arcctg так же получается просто из arctg. Всё дело в том, что они связаны такой формулой: arctg A + arcctg A = П /2 (где П - число Пи = 3.141592653589). Поэтому получить из arctg arcctg несложно. Сложнее с arcsin и arccos. Они связаны между собой таким соотношением: arcsin A + arccos A = П / 2. Однако, как выразить arccos или arcsin через arctg я не знаю. Возможно такая формула и есть, возможно её нет. Мы будем исходить из другого. Конечно, то что будет написанно ниже будет понятно не всем, однако это математика (высшая, а не элементарная) и тут ничего не поделать. Итак ряд Тейлора. Если функция f (x) допускает в некоторой окрестности точки а разложение в степенной ряд по степеням х-а, то этот ряд имеет вид:

f (x) = f (a) + f' (a) (x - a) + f'' (a) (x - a) 2 / 2! + ... + f(n) (a) (x - a) n / n! + ...

где f(n) (a) - n-ая производная, вычисленная в точке а. n! - факторил n. при a = 0 получается ряд Маклорена, которым мы и будем пользоваться. Давайте напишем такой ряд для arcsin:

x + 1/2 * x 3 / 3 + 1 / 2 * 1 / 3 * x5 / 5 + ... + (1 * 3 * 5* .... *(2n - 1) / (2 * 4 * 6 *....*2n) * x 2n + 1 / (2n + 1)

для того, что бы подсчитать arcsin для угла Х, нам надо вычислить эту сумму. Так же естественно, что до бесконечности мы считать её не можем, поэтому считать нужно до какого-то n-ого члена. Чем больше будет это n, тем точнее будет подсчитан arcsin.

function arcsin (x : real) : real;
var
i, j : integer;
result, rs : real;

function ipow (a : real; b : integer) : real;
var
res : real;
begin
res := 1;
for j := 1 to b do
res := res * a;
ipow := res
end;

begin
result := 0;

for i := 0 to 25 do
begin
rs := 1;

for j := 1 to i do
rs := rs * (2 * j - 1) / (2 * j);

result := result + rs * ipow (x, 2 * i + 1) / (2 * i + 1)
end;
arcsin := result
end;

Обратите внимание на дополнительную функцию возведения числа в полжительную целую степень - ipow. До этого мы уже писали функцию для возведения в вещественную степень через логарифм и экспаненту, однако тот же логарифм считается явно медленнее, чем простой цикл. Поэтому если мы уверены, что степень у нас целая и положительная, а мы в этом уверены, то вполне можно использовать такой цикл. Таким образом мы считаем сложную математическую функцию через простые действия - сложение и умножение. Кстати в инжинерных калькуляторах используется тот же алгоритм. Так как мы можем разложить в ряд Тейлора любую функцию, то подсчёт этой функции не составит для вас никакого труда. Следующая задача, называется задачей о Ханойских башнях. Пусть у нас имеется 3 стержня (назовём их X, Y, Z) и N дисков. Наденем все диски на стержень Х в порядке возрастания: в самом низу окажется самый большой диск, в верху самый маленький. Задача состоит в том, что бы перенести эти диски со стержня Х на стержень Y (в том же порядке). Использовать при этом можно только стержень Z. За один раз можно переносить только один диск. Любой диск можно разместить либо на пустом стержне, либо поверх диска большего размера (соответственно вверху всегда должны быть маленькие диски). В решении этой задачи нам поможет рекурсия. Рекурсия - это вызов подпрограммы саму себя. Например, программа для спуска по лестнице smile варианты реализации:

1. в цикле: пока число ступенек не равно 0
2. через рекурсию: спускаемся на одну ступеньку, проверяем не на земле ли мы? да - выход, нет - вызовем себя ещё раз

Давайте рассмотрим пример: пусть процедура выводит сообщение, сколько раз она запустилась, а потом опять рекурсивно себя запускает:

var i : integer;

procedure demo;
begin
i := i + 1;
writeLn ('Произошёл ', i,'-ый вызов процедуры');
demo
end;

begin
i := 0;
demo
end.

подождите запускать эту программу !!! посмотрите на процедуру demo - мы вызываем саму себя, но нигде нет выхода... кажется это что-то типа цикла while 1 do ... будет выполняться вечно, но давайте запустим.... ждём .... оба-на вылазит сообщение Error 202: Stack overflow error. вот вам и вечный вызов smile Что это такое? Пока создам примитивную картинку: есть специальная область памяти (называется стек - Stek) вот её мы и переполнили smile Просто в неё кладётся кое-какая информация о вызове процедур и вызывая процедуру саму из себя, мы постепенно занимаем эту память (освобождается она при выходе из процедуры)... вот как только мы забьём всё что можно и вылазит сообщение об ошибке. Можете убедится, что мы не освобождаем эту память, т.к. не доходим до слова end (жмите F7, если забыли. Глубиной рекурсии называется количество вызовов процедуры(функции) саму себя. В предыдущем примере, мы превысили допустимо возможную глубину и были наказаны ошибкой 202. Так вот для решения задачи о ханойских башнях нам понадобится рекурсия. Лучшим вариантом будет, если вы возьмёте ручку и бумагу и нарисуете 3 палки (это X, Y, Z smile и на Х поставите 3 чёрточки разных размеров. Это башни smile Так вот я сейчас буду объяснять, как решить такую задачу, а вы для наглядности рисуйте. Диски нумеруем так: самый верхний - 1, самый средний 2, самый нижний - 3. Сначала перенесём диск 1 с Х на Y.
потом перенесём диск 2 с Х на Z.
теперь перенесём диск 1 (не забыли где он у нас smile с Y на Z.
Теперь подходим ко второму этапу: переносим диск 3 с Х на Y
переносим диск 1 с Z на Х.
перемещаем диск 2 с Z на Y.
... и заключительный штрих - перемещаем диск 1 с Х на Y.
Всё! поупражнявшись с несколькими башнями можно прийдти к такому алгоритму:

* переместить N-1 дисков с X на Z, используя при этом Y для временного хранения.
* перемещаем N-ый диск с Х на Y.
* перемещаем N-1 дисков с Z на Y, используя X для временного хранения.

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

Program Tower_of_hanoi;

uses CRT;

procedure Hanoi (n : integer; x, y, z : char);
begin
if n <> 0 then
begin
Hanoi (n - 1, x, z, y);
writeLn ('Перемещаем диск ', n, ' c ', x, ' на ', y);
Hanoi (n - 1, z, y, x)
end
end;

var
disk : integer;
begin
ClrScr;
write ('Сколько дисков ?');
readLn (disk);
Hanoi (disk, 'X', 'Y', 'Z')
end.

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

* переместить N-1 дисков с X на Z, используя при этом Y для временного хранения. - Hanoi (n - 1, x, z, y);
* перемещаем N-1 дисков с Z на Y, используя X для временного хранения. - Hanoi (n - 1, z, y, x)

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

Сколько дисков ?3
Перемещаем диск 1 c X на Y
Перемещаем диск 2 c X на Z
Перемещаем диск 1 c Y на Z
Перемещаем диск 3 c X на Y
Перемещаем диск 1 c Z на X
Перемещаем диск 2 c Z на Y
Перемещаем диск 1 c X на Y

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

procedure A;
begin
.....
B;
........
end;

procedure B;
begin
.........
A;
......
end;

Постойте, а откуда компилятор узнает о существовании процедуры B ? Откуда, откуда - от нас smile мы должны сказать компилятору, что типа нечего волноваться, функция описана где-то там, а вот тебе её название, что б случайно не попутался smile Делается с помощью директивы forward, которая и говрит эту фразу компилятору. Директива пишется сразу после описания процедуры (функции). Рассмотрим пример из help'a к Паскалю:

procedure Flip(N: Integer); forward;

procedure Flop(N: Integer);
begin
WriteLn('Flop');
if N > 0 then
Flip(N - 1)
end;

procedure Flip;
begin
WriteLn('Flip');
if N > 0 then
Flop(N - 1)
end;

begin
Flop (5)
end.

Давайте знакомится с халявой Паскаля - обратите внимание на это:

* procedure Flip(N: Integer); forward;
* procedure Flip;

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


 

semen
Дата: Суббота, 30.10.2010, 17:37 | Сообщение #16
Администраторы
Сообщений: 301
Репутация: 150
Длинное Вступление

Здраствуйте!
Сегодня будет длинное вступление. Как вы наверное заметили smile рассылка не выходила в свет чуть больше недели. Я надеюсь, что за это время вы не забыли предыдущие уроки и готовы читать новые. Я вообще считаю, что обучение должно продвигаться не линейно, как учат в школе и институте, а скачками - от одного уровня к другому. При этом перерыв между уровнями может быть очень большой. Итак будем считать, что первый уровень кончился и его мы прошли благополучно. Начинается второе пришествие рассылки smile
К сожалению в прошлом выпуске в программе проверки является ли слово полиндромомом была допущенна ошибка. Правильный вариант программы приведён ниже:

Program Polindrom;

var
str : string;
i, j : integer;
flag : boolean;
begin
i := 1;
flag := true;

readLn (str);
j := ord (str[0]);

while i < j do
begin
if str[i] <> str[j] then
begin
flag := false;
break
end;
{ ВОТ ЭТО БЫЛО ПРОПУЩЕННО !! }
i := i + 1;
j := j - 1
end;

if flag then
writeLn ('ДА !')
else
writeLn ('НЕТ !')
end.

Спасибо Александру, который указал на эту неточность. Кстати он ещё и прислал формулы для выражения arcsin и arccos через arctg, которых я честно не знал:

arcsin(x)=arctg(x/sqrt(1-sqr(x)))

arccos(x)=arctg(sqrt(1-sqr(x))/x)

Редакция рассылки smile приносит свои извинения. Виновные будут найдены и повешены на ближайшем суку.... Да, возвращаясь к предыдущему выпуску. Читая книгу Мартина Гарднера - Математические головоломки и развлечения, нашёл одну интересную историю про задачу о Ханойских башнях, программку для решения которой мы написали в прошлый раз:

Число необходимых перекладываний колец выражается формулой: 2n - 1 (n - число колец)
А вот история: в городе Бенарес (Индия) есть храм, в котором стоят "Пирамиды барминов" (у нас эти пирамиды и назывались - Ханойскими башнями). Как гласит легенда, эта пирамида состоит из 64 золотых колец, которые и по сей день перекладывают жрецы храма. Как только они справятся с этой задачей мир исчезнет smile Такие вот радостные новости, однако не стоит волноваться 264 - 1 = 18 446 744 073 709 551 615 :))) Так, что на наш век хватит и другим ещё останется.

Кстати ещё одна новость для любителей статистики - так как мы перешли в нумерации на Hex числа, то сегодняшний выпуск опять юбилейный - 10 smile
Теория

Я уже наверное вбил в вам в голову, что Паскаль - строготипизированный язык. Вы уже знакомы с довольно большим количеством типов. Однако мы можем и создавать свои типы! Например нам надо создать тип, значение которого будут меняться от 0 до 5. Для описаний типов отведён ещё один отдел программы - type. Он должен находится в самом начале программы, где-то среди var, const и label. Хотя желательно размещать этот раздел первым. Новый тип объявить очень просто, как константу:

Имя = тип

Ну не нравится мне, например, каждый раз писать длинное слово integer, а хочется чего-то покороче - int. Пожалуйста:

type
int = integer;
var
i : int;

Естественно, что наш "новый" тип int сохранит все свойства типа integer - максимальное и минимальное значение, допустимые операции. Мы просто обозвали integer как int, по этому теперь эти переменные имеют одинаковый тип:

type
int = integer;
var
i : int;
j : integer;
begin
i := 1;
j := i;

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

type
TColor = (red, green, blue);
var
color : tcolor;

Теперь мы можем присваивать переменной color значения red, или green или blue вот так: color := red; Для численного представления элементов перечисляемого типа используется уже известная функция ORD. Причём первому элементу соответствует 0, второму 1 и так далее... Т.е. ord (red) = 0, ord (green) = 1, ord (blue) = 2. Допускается и обратное преобразование типов из целых в перечисляемые. Делается с помощью указаня имени типа и в скобках целого значения:

color := tcolor (1); - равносильно color := green;

Функции преобразования создаются автоматически. Давайте познакомимся ещё с несколькими функциями языка Паскаль:

function pred(X): - возвращает предшественник аргумента

т.е. предыдущее значение типа. Например

pred (1) = 0
pred (blue) = green

function Succ(X): - возвращает следующее значение типа

Например:

succ (0) = 1
succ (green) = blue

Кстати переменную перечисляемого типа можно объявлять без объявления типа, сразу разделе var:

var
color : (red, green, blue);

Перечисляемый тип это конечно хорошо, а если нужно содать тип в котором будет больше 100 значений (!) тут он явно не подойдёт. Тут нам поможет тип-диапазон. Тип-диапазон - это подтип (подмножество) базового типа, которым может быть любой целый тип, кроме типа-диапозона (логично, не правда ли :). Тип-диапазон задаётся своими границами: мин_значение .. макс_значение. Две точки .. рассматриваются как один символ и неотделимы! Например создадим тип-диапазон, который будет менять значения от -25 до 32:

type
tsome = -25 .. 32;
var
c : tsome;
begin
c := 0;

При этом -25 <= c >= 32 Присвоение с := 33 вызовет ошибку. Можно создавать и такие типы-диапозоны:

type
Tday = 1..31;
Tmonth = 1..12;
Tabc = 'A' .. 'Z';
var
buka : Tabc;
begin
buka := 'D';
buka := chr (66);
writeLn (buka)
end.

тип Tabc является под-типом char, поэтому над ним разрешаются только операци допустимые типом. Такое неправильно: buka := 66 !!! Рассмотрим ещё один пример:

type
Tweek = (mo, tu, we, th, fr, sa, su);
TweekEnd = sa .. su;
var
w : TweekEnd;

begin
w := sa;
w := pred (su);
writeLn (ord (w));
w := succ (w);
writeLn (ord (w))
end.

ну и что же выведет такая программа? На самом деле - 5 и 6. Так как не стоит забывать, что первому элементу Tweek соответствует 0, а последнему 6. А TweekEnd у нас является под-типом Tweek и соответственно сохраняет значения элементов. Напомню, что в Паскале есть ещё две функции, которые работают с типами:

function High(X) - возвращает максимальное значение типа-диапозона, к которому принадлежит переменная Х
function Low(X); - соответственно возвращает минимальное значение типа-диапозона, к которому принадлежит переменная Х

Программа

Возиожно это и неочивидно, но ведь мы можем создать тип функции или тип процедуры. Делается это так же, как и для типа нужно объявить таким образом:

Имя = function (параметры) : тип_возвращаемого_значения;
имя = procedure (параметры);

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

имя = array [диапозон] of тип;

Рассмотрим такой пример:

program fillarray;

uses CRT;

const
N = 10;

type
Tarray = array [1 .. N] of integer;
Tfunc = function : integer;

procedure fill (var res : tarray; f : Tfunc);
var
i : integer;
begin
for i := 1 to N do
res[i] := f;
end;

procedure printarray (res : tarray);
var
i : integer;
begin
for i := 1 to N do
write (res[i], #32)
end;

function f1 : integer; far;
begin
f1 := random (256)
end;

function f2 : integer; far;
begin
f2 := random (100)
end;

var
A : tarray;
begin
randomize;
ClrScr;
fill (A, f1);
printarray (A);
writeLn;
fill (A, f2);
printarray (A)
end.

Итак пусть нам надо написать процедуру для заполнения массива, каждый элемент массива - это значение, возвращенное некоторой функцией, которые в свою очередь разные. Вот блин загнул smile Этим у нас и займётся процедура procedure fill (var res : tarray; f : Tfunc); - первый параметр - это массив, второй - функция, с помощью которой мы этот массив и заполним. Что бы передать в качестве параметра функцию, нужно просто указать её имя. А при описании функции использовать директиву far. Что это такое я раскажу позже. Пока, что запомните, что эта директива позволяет выступать функции в качестве параметра. В процедуре fill мы объявляем параметр f, как тип-функцию. Теперь внутри процедуры fill мы можем обращаться с ней так, как будто бы такая функция действительно есть, т.е. мы можем вызвать её, указав имя - f. Если бы были и параметры, то можно было бы передать их как то так: f (par1, par2); Мы как бы создаём виртуальную функцию, которая потом замещается настоящей. Такие сложности на самом деле окупаются. Техника передачи параметров функций поможет создавать различные варианты для программы - от примитивного заполнения массива, до расчёта сложных физических процессов.
Факториал

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

Program Factorial;

function fact (n : integer) : longint;
begin
if n <= 1 then
fact := 1
else
fact := fact (n - 1) * n
end;

var
a : integer;
begin
Write ('Введите число:');
ReadLn (a);
WriteLn ('Факториал ', a, '! = ', fact (a) )
end.

В чём заключается идея? В такой простой формуле:

n! = (n - 1)! * n

Соответственно мы и останавливаемся при n = 0 (или 1) : 0! = 1 (1! = 1)


 

semen
Дата: Суббота, 30.10.2010, 17:37 | Сообщение #17
Администраторы
Сообщений: 301
Репутация: 150
Давайте представим, что нам нужно создать базу данных с книгами. При этом о каждой книге нам надо знать следующее: имя автора, название, количество страниц. Что тут делать? Нам помогут записи. Запись - это структура данных, состоящая из некоторого числа компонентов, которые называются полями. Записи объявляются в разделе типов. Так сами являются типом данных. Потом мы можем создавать переменные таких типов. Запись объявляется таким образом:

ИМЯ = RECORD поля END

Поля записей - это переменные любого типа (в том числе и записи !). Объявляются они так же как и в разделе переменных:

type
TBook = record
author, name : string;
page : integer
end;

var
book, b1 : TBook;

Таким образом в записи TBook у нас 2 поля типа string и одно поле типа integer. Для одинаковых записей возможно применение операции := Наприер мы можем сделать так book := b1; доступ к полям осуществляется таким образом: имя_переменной.имя_поля Т.е. через точку! Например так:

begin
book.author := '****';
book.name := 'Pascal-Tutorial';
book.page := 17;

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

with переменная do оператор

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

with book do
begin
author := '****';
name := 'Pascal-tutorial';
page := 17;
writeLn ('Author - ', author, ' Name - ', name, ' # - ', page)
end

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

function SizeOf(X): Integer;

возвращает размер аргумента Х в байтах. Например: writeLn (sizeof (book) ) выведет на экран 514 - можно подсчитать, что это правда: сосчитайте отдельно размер каждого поля - author и name по 256 байт (так как строки это array [0 .. 255] of char - а char занимает 1 байт => размер равен числу элементов массива - 256). Размер page = 2 байта. Так мы получаем: 256 + 256 + 2 = 514.
Программа

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

* Имя автора
* Название книги
* Число страниц
* Цена
* Дата издания

конечно можно было бы добавить и ещё что-то, но нам и этого хватит. Кстати дату тоже можно организовать в виде записи. С этого и начнём:

type
Tdate = record
d, m, y : integer
end;

Чудненько! Теперь ничто нам не мешает создать запись, которая будет отвечать за книгу:

Tbook = record
author, name : string;
page, cost : integer;
date : tdate
end;

Итак заметьте, что поле date в свою очередь является записью. Пусть у нас есть переменная с именем book типа tbook. Тогда что бы обратиться к месяцу нужно написать такое: book.date.m !
Ну и что бы было совсем круто smile опишем запись библиотеки, в которой будут массив из книг (то бишь типа Tbook) и число книг в библиотеке.

Tlib = record
book : array [1 .. Max] of Tbook;
num : byte
end;

где Max - константа, отвечающая за вместимость библиотеки. Напишем програмку для малюсенькой библиотеки книг этак на 50.

program homelib;

uses CRT;

const
Max = 50;

type
Tdate = record
d, m, y : integer
end;
Tbook = record
author, name : string;
page, cost : integer;
date : tdate
end;
Tlib = record
book : array [1 .. Max] of Tbook;
num : byte
end;

function Menu : char;
begin
ClrScr;
writeLn (' БИБЛИОТЕКА.');
writeLn (' 1 - добавить книгу.');
writeLn (' 2 - показать книги.');
writeLn ('ESC - выход.');
Menu := readkey
end;

procedure Add (var new : tlib);
begin
if new.num = Max then
begin
writeLn ('Нет места на полках :(');
readLn;
exit
end;
new.num := new.num + 1;
with new.book[new.num] do
begin
Write ('Введите имя автора: ');
readLn (author);
write ('Введите название: ');
readLn (name);
write ('Введите число страниц: ');
readLn (page);
write ('Введите цену: ');
readLn (cost);
write ('Введите дату ДД.ММ.ГГГГ: ');
with date do
begin
readLn (d, m, y)
end
end
end;

procedure Show (lib : tlib);
var
i : integer;
begin
for i := 1 to lib.num do
begin
writeLn ('===========================');
writeLn ('Книга номер ', i);
with lib.book[i] do
begin
writeLn ('Автор ', author);
writeLn ('Назвние ', name);
writeLn ('Число страниц ', page);
writeLn ('Цена = ', cost);
with date do
writeLn ('Дата ', d, '.', m,'.', y)
end
end;
readLn
end;

var
lib : tlib;
key : char;

begin
lib.num := 0;

while key <> #27 do
begin
key := Menu;
case key of
'1': Add (lib);
'2': show (lib)
end
end
end.

Итак быстренько копируем запускаем... круто ошибок нет ! ... нажимаем 1, вводим все поля... жмём 2... чёрт ! А это что ещё такое:
Error 202: Stack overflow error.
Итак перед вами сообщение о переполнении стека (Stack). Где же оно возникает? давайте выполним пошагово с заходом в функции: жмём F7.... вводим данные... опа! вот оно! На входе в процедуру show. Вы помните, что существуют два типа передачи параметров: адрессный и простой. При простом способе в памяти создаётся копия переменной и используется. После выхода из процедуры (функции) эта память освобождается. Так же в этой памяти создаются локальные переменные и хранятся некоторые данные о программе. Эта область памяти и называется стеком. Давайте разберёмся почему происходит её переполнение. Нажмите Ctrl+F4 (из меню Debug -> Evaluate/modify...) перед вами возникнет окошко. В поле Expression можно вводить выражение, которое надо подсчитать. Давайте подсчитаем размер нашей структуры tlib: введём в это поле sizeof (tlib) и посмотрим на результат (поле Result) - там будет размер записи tlib в байтах, а именно 26 101. Какой же у нас размер стека ? Это можно узнать таким образом: Options -> Memory sizes...
Перед вами открылось окно с непонятными названиями и цифрами. Причём подсвечен именно размер стека (Stack size). Если вы ничего не меняли, то он должен быть равен 16 384. Кстати обратите внимание, что слова Stack Size упоминаются 3 раза: первый раз в разделе Real Target, второй - Protected Target, и наконец третий - Windows Target. Наши программы пока относятся к Real (ведь они такие реальные:). Про остальные две цели smile (target - цель англ.) я раскажу позже. Сейчас запомните, что мы пишем программы для реального режима (Real mode) процессора (не правда ли звучит гордо smile Поэтому нам и надо Real target. Подробности как всегда будут позже. Такой я нехороший. Так вот вернёмся к нашим баранам. Размер стека стоит 16 384, а размер одной нашей структуры уже 26 101. Не правда ли большая разница ? Давайте увеличим размер стека. Для этого просто введите его размер. Давайте и введём 26101. Запускаем программу .... (для этого вместо привычных Ctrl+F9 нужно нажать сначала F9, а уж потом Ctrl+F9) досада! опять та же ошибка. Однако давайте вспомним, что я написал немного выше - в стеке хранятся и локальные переменные и некоторые служебные данные. Поэтому нам нужно увеличить его размер ещё немного. Давайте не пожадничаем и увеличим размер на 1 килобайт. После этих действий программа должна работать нормально. Вернёмся теперь к сути программы.
Итак у нас программа состоит из 2-х процедур и одной функции. Функция Menu у нас "рисует" на экране меню и возвращает нажатую клавишу. В главной программе в зависимости от этого добавляем книгу, выводим данные о всех книах или выходим (этот случай когда key = #27. 27 - код клавиши ESC). Давайте теперь обратим наш взгяляд на процедуру Add. В ней мы заполняем запись new (обратите внимание на вложенность операторов with). Обращать внимание больше не что smile Думаю, что исходник довольно прозрачный, однако если вам что-то не понятно, то обязательно напишите.
Как вы яхту назовёте, так она и поплывёт!

Думаю вы уже обратили внимание, что я начинаю название всех типов с буквы t. Это сокращение от слова Type. Так ведь удобнее и сразу поймёшь, где у тебя тип, а где что-то ещё. Однако некоторые пошли дальше и используют подобные сокращения у всех имён. Например названия функций начинают с букв fn, переменных целого типа с i, строк - sz и т.д. Это так называемая Венгерская нотация. Она активно используется при программирование в Windows, т.к. была разработана Чарзом Симони (программист Microsoft). Она имеет свои выгоды: вы всегда по имени переменной понимаете, какого она типа. Однако никто не заставляет ей пользоваться. Хотя я и перенял вредную привычку начинать новые типы с буквы t.


 

semen
Дата: Суббота, 30.10.2010, 17:38 | Сообщение #18
Администраторы
Сообщений: 301
Репутация: 150
Думаю все вы имеете представление о файлах. Сегодня мы научимся работать с ними программно. Что из себя представляет файл? Последовательность из байтов. Давным давно, когда компьютеры были большими smile использовались магнитофонные кассеты для хранения информации. Так вот кассета наглядно представляет собой файл: байты идут друг за другом, как песни. Типа так:
0 байт 1 байт 2 байт 3 байт 4 байт 5 байт 6 байт 7 байт 8 байт ....

Соответственно наша задача на сегодня - научиться читать и писать эти байты. Файл в некотором приближении напоминает массив. Работа с файлом традиционно заключается в следующих действиях:

1. открытие файла
2. чтение/запись/ничего не делание smile
3. закрытие файла

Думаю вам привычна работа с именем файла. Копируя, удаляя или делая ещё что-то вы указываете имя. В Паскале работа с файлом ведётся с помощью файловых переменных. Эти переменные связываются с файлом (с помощью имени) и потом используются только они. Итак с начала мы должны создать файловую переменную. Для этого нам нужно указать её тип. Тип файла описывается так:

file of тип

Например нам нужно описать файл с целыми числами. Тогда мы создадим такой тип:

type
tIntFile = file of integer;
tByteFile = file of byte;

второй тип - это тип для файла из байтов. Создав тип мы соответственно можем создать и файловую переменную:

var
f : tByteFile;

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

procedure Assign(var f; String);

Первый параметр - это и есть наша файловая переменная. Второй - это имя файла. Имя файла должно соответствовать требованиям операционной системы. Задаётся оно в виде DRIVE:\path\...\name.expansion Здесь (например для файла на диске С в папке bp с именем readme.txt):

* Drive - диск (буквы от A до Z) (у нас это С)
* path - путь (у нас это bp)
* name - имя файла (у нас - readme)
* expansion - расширение (у нас - txt)

поэтому получается такое имя файла - C:\bp\readme.txt Что бы создать новый файл нужно просто указать не существующее имя. Итак файл связан с файловой переменной, что дальше? Дальше нам надо открыть файл. Для этого существуют следующие процедуры:

* procedure Reset(var F [: File; Recsize: Word ] ); - пока не обращайте внимание на то что находится в квадратных скобках. Для нас процедура выглядит так: procedure Reset (var f);
Открывает файл, связанный с файловой переменной f, для чтения. Если такого файла не существует (не можем же мы читать не существующий файл !) , то возникает ошибка ввода-вывода. Об этой ошибке мы поговорим ниже smile
* procedure Rewrite(var F: File [; Recsize: Word ] ); - так же не обращаем внимание на указанное в квадратных скобках.
Эта процедура открывает новый, пустой файл для записи, ему присваивается имя, которое мы указали в вызове Assign. Если файл с таким именем уже существует, то он уничтожается.

Перенесёмся сразу же на закрытие файла, перепрыгнув запись/чтение/ничего не делание smile Закрывается файл с помощью процедуры:

procedure Close (var F);

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

function IOResult: Integer;

возвращает целое число, соответствуешее последней ошибке ввода-вывода (отсюда и название: Input-Output Result - результат ввода-вывода, англ.). Если ошибки нет, соответственно вернёт 0. При этом фунция работает только при выключенном режиме контроля ошибок. Например напишем такую программку:

program test;

type
tfile = file of byte;

var
f : tfile;

begin
assign (f, 'C:\bla.bla'); {связываем f c файлом C:\bla.bla }
reset (f); {пытаемся открыть его для чтения }
writeLn ('Bla-bla-bla!!!') { делаем что-то }
end.

Если у вас нет файла C:\bla.bla то соответственно программа завершится с ошибкой 2 : File not found (файл не найден). Однако такое развитие программы нас (по крайней мере меня) не устраивает. Что же это будет, если программа каждый раз будет завершаться с надписью run time error 2 ??? пользователи явно ничего не поймут. Поэтому нам нужно отключить контроль ошибок ввода-вывода. Итак лезем сюда Options -> Compiler... В появившемся диалоге ищем I/O checking. Если крестик стоит - значит контроль есть, если не стоит - нету. Однако сносить крестик с радостным криком не стоит. Вполне возможно что вам впоследствии понадобится проверка ошибок ввода-вывода. Так что мы пойдём другим путём. Помните, я говорил про штуки которые похожи на комментарии, но таковыми не являются ? Они начинаются с {$ и заканчиваются } похоже на комментарий. Это диррективы компилятору. Мы можем явно указать, где нужна проверка ошибок ввода-вывода, а где нет. Включение проверки ошибок делается с помощью директивы {$I+} выключение - {$I-}. Теперь перепишем нашу программку так:

program test;

type
tfile = file of byte;

var
f : tfile;

begin

{$I-}
assign (f, 'C:\bla.bla');
reset (f);
close (f);
{$I+}

if IOResult = 0 then
writeLn ('bla-bla-bla')
else
writeLn ('file not found')
end.

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

procedure Read(F , V1 [, V2,...,Vn ] );

Первый параметр f - это файловая переменная, а V1 и т.д. - происходит считывание данных из файла в эти переменные. Переменные должны быть того же типа, что и файл. (т.е. если file of byte, то и Vn должно быть типа byte). При чтении файла, нам нужно проверять не достигнут ли конец ? Концом файла является специальный символ, называемый EOF - EndOfFile (конец файла - англ.). Узнать достигнут ли конец файла, можно вызвав одноимённую функцию:

function Eof(var F): Boolean;

F - файловая переменная. Функция возвращает true, если конец файла достигнут и false в противном случае. Таким образом чтение файла нужно производить в цикле - прочитали что-то, проверили не конец ли. Если не конец, тогда прочитали дальше. Например напишем программу для чтения и вывода на экран текстового файла. Для простоты я указал имя C:\autoexec.bat если у вас нет такого файла, то укажите какой-либо другой:

Program readfile;

type
tfile = file of char;

var
f : tfile;
c : char;

begin
assign (f, 'C:\autoexec.bat');
reset (f);

while not EOF (f) do
begin
read (f, c);
write ©
end;

close (f)
end.

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

procedure Write(F, V1 [, V2,...,Vn ] );

где F - это файловая переменная, а V1 ... Vn - это список переменных, значения которых мы должны записать в файл. Для примера напишем программу, которая копирует один файл в другой. Что нам для этого надо? Считать байт из одного файла - записать байт в другой и так до конца. Сохраним копию autoexec.bat в autoexec.txt:

Program copyfile;

type
tfile = file of byte;

var
fin, fout : tfile;
c : byte;

begin
assign (fin, 'C:\autoexec.bat');
reset (fin);
assign (fout, 'C:\autoexec.txt');
rewrite (fout);

while not EOF (fin) do
begin
read (fin, c);
write (fout, c)
end;

close (fin);
close (fout)
end.

Программа

Думаю понятно, что тип у файла может быть не только предопределённый, но любой созданный нами. Сегодня мы создадим базу данных для учителя smile Она будет содержать имена и оценки учеников. Для каждого ученика создадим запись tstudent с этими данными.

type
tstudent = record
name : string;
result : byte
end;

После этого создадим тип файла, состоящего из таких записей:

tfile = file of tstudent;

после записи в файл мы ради эксперимента подсчитаем число отличников. Для этого нам надо закрыть файл (т.к. мы открыли файл на запись), а потом открыть его на чтение.

Program studentdata;

const
N = 3;

type
tstudent = record
name : string;
result : byte
end;

tfile = file of tstudent;

var
f : tfile;
i : integer;
man : tstudent;

begin
assign (f, 'E:\result.txt');
rewrite (f);

for i := 1 to N do
begin
write ('Введите имя: ');
readLn (man.name);
write ('Введите оценку: ');
readLn (man.result);
write (f, man)
end;

close (f);

reset (f);
i := 0;

while not EOF (f) do
begin
read (f, man);
if man.result = 5 then
i := i + 1
end;

writeLn ('Число отличников: ', i);
close (f)
end.

думаю, что текст программы довольно прозрачен.


 

semen
Дата: Суббота, 30.10.2010, 17:38 | Сообщение #19
Администраторы
Сообщений: 301
Репутация: 150
Сегодня мы поговорим о бестиповых файлах. Бестиповые файлы позволяют записывать произвольные данные в файл. Для создания файловой переменной, мы должны указать просто слово file:

var
F : file;

Принцип работы с бестиповыми файлами такой же, как и с типовыми: сначала связываем файловую переменную с файлом с помощью процедуры Assign. Затем открываем его с помощью процедур Reset или Rewrite. А после окончания работы он должен быть закрыт процедурой Close. Однако не всё так просто, как хотелось бы sad Помните в прошлый раз я написал дополнительные параметры к функциям Rewrite и Rest ? Для типовых файлов эти параметры были не важны, а вот для бестиповых.... так, что читайте ниже дополнительное описание этих процедур:

* procedure Reset(var F : File; Recsize: Word );
* procedure Rewrite(var F: File ; Recsize: Word );

в обоих случаях параметр Recsize задаёт число байтов, считываемых из файла или записываемых в него за одно обращение. Минимальное значение Recsize 1 байт, максимальное - 64 К байт. По умолчанию он равен 128 байтам. А вот запись и чтение осуществляется другими процедурами.

procedure BlockRead(var F: File; var Buf; Count: Word; var Result: Word);

Эта процедура служит для чтения из файла. F - файловая-переменная. Buf - переменная любого типа, Count - число блоков, которое нужно считать в Buf. Необязательный параметр Result - число успешно прочитанных блоков. В случае успеха Result = Count. Например считаем из файла строку:

program test;

var
FromF: file;
Buf : string;

begin
Assign(FromF, 'C:\1.txt');
Reset(FromF, 1);
BlockRead (FromF, Buf, SizeOf(Buf));
Close(FromF)
end.

Мы не используем параметр Result при вызове BlockRead. Размер нашего буффера можно сосчитать с помощью уже известной функции sizeof. Кстати, если вы напишите после этого строку write (buf), то увидите совсем не строку из файла, а нечто на неё похожее. BlockRead до лампочки читаете вы из файла строку или вещёственное число, она просто читает заданное ей количество байт и записывает их по аддресу переменной buf. Т.е. мы дали команду считать 256 байт (SizeOf(Buf) = 256) и записать результат в Buf. BlockRead не будет разбираться, что Buf у нас это строка, и не запихнёт в Buf[0] размер строки. В buf[0] будет первый байт, находящийся в файле! А это может быть, например нулевой символ и тогда buf[0] = #0 и на экран ничего не выведется. Ну и соответственная процедура для записи в файл:

procedure BlockWrite(var f: File; var Buf; Count: Word; var Result: Word);

Соответственно эта процедура пишет в файл F, Count байт из Buf. Result так же является не обязательным параметром, и в нём возвращается число блоков успешно записанных процедурой. Например запишем в файл строку, которую ввёл пользователь:

var
FromF: file;
Buf : string;

begin
ClrScr;
Assign(FromF, 'C:\2.txt');
rewrite(FromF, 1);
readLn (buf);
BlockWrite(FromF, Buf, SizeOf(Buf));
Close(FromF)
end.

BlockWrite так же по барабану строка это у нас или нет. Поэтому в результате создасться файл, размером в 256 байт. Даже если вы ввели строку из 3-х символов, размер не изменится. При этом первый байт в файле - это символ с номером, равным длинне строки. Однако рамер одного символа = 1 байту, размер строки = buf [0], поэтому преобразовав вызов процедуры в такой, мы получим нужный результат (т.е. такой результат, что бы размер файла соответствовал размеру строки + 1):

BlockWrite(FromF, Buf, ord (Buf[0]) + 1);

Кстати практически все файлы являются не типизированными. Откройте, например, любой bmp через текстовый редактор. Вот так выглядят несколько строк любимых Облачка.bmp:

BM:_ : ( А р   _ + - A A +н{ _¦Д +нs _н{ _+Ф _¦{ _¦М _+М _¦Ь я_+ яч_ ч¦е ч++ ч+н ч+е ўяч ячч _+Ь +¦{ _нs я_¦ ч+¦ ўўў ўяя _+Д ўўя ч+¦ яч+ ч+¦ ч¦Ь яч+ я++ яя_ ўч_ ўя_ ўўч _¦s я+¦ ч+Ь ўч+ я_+ я_+ я+¦ _ўў ч+Ф _¦Ф я+¦ ч¦н __ў ўў_ ў_ў ў_+ ўч+ ___ ч+н ч¦Ф _ў_ я+н ч+Ь ч+М ў__ я+н ч¦М ч¦Д я+е

а ведь и не скажешь, что картинка smile
Программа

Сегодня мы не будем особо выдумывать и напишем очередную програмку, которая копирует один файл в другой. Естесственно мы будем считать, что оба файла не типизированные. И как всегда smile скопируем autoexec.bat в autoexec.txt:

program CopyFile;

var
FromF, ToF: file;
NumRead, NumWritten: Word;
Buf: array [1..100] of Char;

begin
Assign(FromF, 'C:\autoexec.bat');
Reset(FromF, 1);
Assign(ToF, 'C:\autoexec.txt');
Rewrite(ToF, 1);

repeat
BlockRead(FromF, Buf, SizeOf(Buf), NumRead);
BlockWrite(ToF, Buf, NumRead, NumWritten)
until (NumRead = 0) or (NumWritten <> NumRead);

Close(FromF);
Close(ToF)
end.

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

BlockRead(FromF, Buf, SizeOf(Buf), NumRead);

в NumRead у нас окажется число прочитанных байт, потом мы вызываем BlockWrite:

BlockWrite(ToF, Buf, NumRead, NumWritten)

Поэтому даже если мы прочитали меньше, чем sizeof (Buf) (ну ка скажите моментом, чему равено sizeof (buf)? если вы потратили на это больше двух секунд, значит вам нужно больше заниматься :). Так вот даже если мы прочитали меньше, чем sizeof (Buf), то запишем мы ровно столько, сколько прочитали


 

semen
Дата: Суббота, 30.10.2010, 17:39 | Сообщение #20
Администраторы
Сообщений: 301
Репутация: 150
Сегодня мы закончим повествование о работе с файлами. Особое место в Паскале занимают текстовые файлы. Не путайте с файлами, у которых расширение txt. У текстовых файлов может быть любое расширение (в том числе и txt :). Давайте определимся, что мы будем иметь в виду, говоря "текстовый файл".

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

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

var
F : Text;

Открываются и закрываются текстовые файлы так же как и типовые. Соответственно процедуры для чтения и записи те же. Так же можно считать/записать строку целиком с помощью процедур ReadLn / WriteLn, указав первым параметром файловую переменную. Однако для работы с текстовыми файлами введены дополнительные функции и процедуры:

* function Eoln (var F: Text) : Boolean; Эта функция принимает значение trUE, если достигнут конец строки, и значение FALSE, если конец строки не достигнут. Название от англиского: EndOfLiNe - конец линии (строки).
* function SeekEoln (var F: Text) : Boolean; Возвращает значение trUE, если до конца строки остались только пробелы, и FALSE в противном случае.
* function SeekEof (var F: Text) : Boolean; Возвращает значение trUE, если до конца файла остались только строки из пробелов, и FALSE в противном случае.
* procedure Append(var f: Text); открывает текстовый файл для дозаписи в конец файла.
* procedure SetTextBuf(var F: Text; var Buf; Size: Word ); служит для увеличения или уменьшения буфера ввода - вывода текстового файла f. Значение размера буфера для текстовых файлов по умолчанию равно 128 байтам. Увеличение размера буфера сокращает количество обращений к диску. Рекомендуется изменять размер буфера до открытия файла. Буфер файла начнется с первого байта переменной Buf. Размер буфера задается в необязательном параметре Size, а если этот параметр отсутствует, размер буфера определяется длиной переменной Buf.
* procedure Flush(var F: Text); применяется к открытым для записи файлам. Она принудительно записывает данные из буфера в файл независимо от степени его заполнения.

Несмотря на такое внимание к текстовым файлам принципы работы с файлами остаются прежними. Поэтому сегодняшний выпуск посвящён им только наполовину. Вторую часть сегодняшнего повествования отведём "путешествиям" по файлам. Вы наверно уже заметили, что до сих пор мы считывали данные из файлов последовательно. Т.е. считав k блок мы переходили к k+1 блоку. А что делать, если надо считать k+2 блок ? Конечно можно считать k+1 в "никуда". Однако если нам потребуется считать не k+2, а k+100000 smile работа с файлами по компьютерным меркам ведётся довольно медленно и считывыя бесполезные данные мы создадим тормозную программу. Думаю вам этого не хочется. Так вот на самом деле мы можем позиционироваться в файле. Т.е. переходить сразу к нужному нам блоку. Файл условно разбивается на n - блоков. Первому блоку соответствует номер 0. Последнему - n-1. Блок с номером n - является концом файла. Будем называть номер блока - смещением относительно начала файла. Для определения размера файла в блоках используется функция:

function FileSize(var F): Longint;

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

type
fb = file of byte;
fi = file of integer;
var
f1 : fb;
f2 : fi;
begin
Assign (f1, 'E:\Result.txt');
Assign (f2, 'E:\Result.txt');
reset (f1);
reset (f2);
writeLn ('Size of f1 - ', FileSize (f1));
writeLn ('Size of f2 - ', FileSize (f2))
end.

результат на экране будет различным. Почему? Для хранения переменной типа byte в памяти отводится 1 байт, и следовательно файл будет представлен, как последовательность блоков, размером в 1 байт. Поэтому FileSize (f1) даст нам размер файла в байтах. Для хранения же переменной типа integer отводится 2 байта, поэтому FileSize (f2) даст результат в 2 раза меньше, т.к. один блок будет у нас 2 байта. При считавание данных из файла смещнение относительно начала меняется. Т.е. считали одно число - смещение +1, считали два - смещение +2 и т.д. Что бы узнать смещение относительно начала файла применяется функция:

function FilePos(var F): Longint;

для только что открытого файла смещение (а значит и результат FilePos) будет равно 0. Для файла дочитанного до конца FilePos = FileSize.

procedure Seek(var F; N: Longint);

эта процедура позволяет передвигать текущую позицию по файлу (номер блока указывается в N). Например начало файла: Seek (F, 0), конец Seek (F, SizeOf (F)).

procedure truncate(var F);

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


 
  • Страница 1 из 2
  • 1
  • 2
  • »
Поиск: