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

Программирование на Delphi

  Все выпуски  

Программирование на Delphi (выпуск 87)


Программирование на DELPHI
Рассылка сайта www.delphi.int.ru
Выпуск #87 (22 августа 2009 г.)

Разделы сайта:

Delphi.int.ru

Новости сайта
Файловый архив
Статьи
Компоненты
Plug-in's
Документация
Исходники
Изображения
Игры
Программы
Рассылки сайта
Гостевая книга
F.A.Q.
Регистрация
Вставить код

IRC-канал сайта:

Сеть: DalNet
Сервер: irc.dalnet.ru
Порт: 6667
Канал: #delphiintru

Ждём Вас на нашем канале!

Доброго времени суток, уважаемые читатели!

За летний период на сайте произошло несколько значимых нововведений и структурных изменений.

I. Созданы новые тематические разделы:

C++ - язык C/C++ и все его разновидности и реализации.
Web-программирование - название раздела говорит само за себя, а вот его подразделы:
PHP
Perl
Среда разработки Delphi for PHP
Java Script
Технология AJAX
Прочее (web)
Лабораторный практикум - время показало, что такой раздел необходим. Сюда относятся все вопросы, в которых авторы не очень заинтересованы в решении конкретной проблемы и где целью является помощь/просьба в выполнении учебного задания.
Разные вопросы - для вопросов, не относящихся ни к одному из имеющихся тематических разделов.

II. На сайте появилась новая функция - Вставка кода. Этот сервис позволяет сохранять фрагменты кода (или просто текста) и делиться ими с другими пользователями. Например, общаясь в ICQ или IRC, не очень удобно отсылать большие блоки программного кода. Данный сервис легко решает подобные проблемы. В сети существует множество подобных "корзин" и теперь такая возможность есть и на нашем сайте. Так сказать «не отходя от кассы».
Сервис доступен всем посетителям сайта, в т.ч. и незарегистрированным. Однако, зарегистрированным пользователям доступны более широкие возможности.
Для вставки кода следует пройти по следующей ссылке: http://www.delphi.int.ru/paste Для удобства эта ссылка расположена в основной навигации на страницах сайта. Желаем удобного использования нового сервиса!

III. В популярной социальной сети вКонтакте появилась группа нашего сайта: Delphi.int.ru - программирование (на Delphi и не только). В группе публикуются различные видео- и фото-материалы. Также там можно вести обсуждения с другими участниками на темы программирования и делиться мнениями о сайте. Присоединяйтесь!

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

В этом выпуске:

  • Обучение - урок 26: Записи (часть 2);
  • Статья: Леворекурсивный парсер;
  • Файловый архив - новые компоненты, исходники, программы и игры;
  • Юмор - 50 причин, почему компьютеры лучше человека.

Статистика Delphi.int.ru Expert на 22.08.2009, 12:00 (предыдущий подсчёт - 22.02.2009, 20:00):

Количество экспертов: 83 (+9).
Участниками задано вопросов: 3110 (+667).
Экспертами дано ответов: 3371 (+369).
Количество сообщений на мини-форумах: 10476 (+3228).

До встречи в следующем выпуске!

Разделы рассылки:

» Авторское слово
» Обучение Delphi
» Delphi.int.ru Expert
» Статья по Delphi
» Файловый архив
» Юмор

Delphi.int.ru Expert
Сообщество программистов: общение, помощь, обмен опытом.

Количество читателей рассылки (22.08.2009, 12:00):
5378+2018+471= 7867

Связь по e-mail:

admin@delphi.int.ru
support@delphi.int.ru


Если Вы хотите где-либо разместить материалы, представленные на www.delphi.int.ru или в данной рассылке, свяжитесь, пожалуйста, с их автором или ведущим рассылки.

Delphi.int.ru Expert
Сообщество программистов: общение, помощь, обмен опытом
(текущая версия системы: 2.6+; последнее обновление: 08.08.2009)

Последние новости

5 июня 2009 г.

Расширены возможности тега цитирования - <quote>. Добавлены дополнительные параметры, которые конкретизируют автора или источник цитаты: user - ID участника, name - имя человека или название источника цитаты.

7 августа 2009 г.

В форму добавления сообщений в мини-форум вопроса интегрирован визуальный HTML-редактор.

 

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

За февраль медали получили: Amidamaru (лучший ответ) и Мережников Андрей (лучший вопрос).
По итогам марта: Anrie (лучший вопрос), а также min@y, Ученый и Dron - разделили место лучшего ответа.
За апрель: Вадим К (лучший ответ) и Ученый (лучший вопрос).
За май: Александр Дубик (лучший ответ) и Ruslan (лучший вопрос).
По итогам июня: Паровоз (лучший ответ) и Pasha (лучший вопрос).
За июль: Егор (лучший ответ) и Failure (лучший вопрос).

Последние достижения:

25 июля 2009, 19:59: Ученый повысил свой уровень и теперь имеет статус 8-ой класс (прежний статус: 7-ой класс).

23 июля 2009, 13:06: Лед и Пламень стал экспертом и теперь имеет статус 1-ый класс (прежний статус: Посетитель).

13 июля 2009, 16:48: Егор стал экспертом и теперь имеет статус 1-ый класс (прежний статус: Посетитель).

13 июля 2009, 14:07: Паровоз повысил свой уровень и теперь имеет статус 4-ый класс (прежний статус: 3-ий класс).

8 июля 2009, 09:25: Виталий повысил свой уровень и теперь имеет статус 2-ой класс (прежний статус: 1-ый класс).

 

Архив: вопросы и ответы

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

Статистика по выпуску:

Кол-во вопросов из выбранного диапазона: 30
Вопросов без ответов: 3
Кол-ответов: 39
Баллов за ответы: 95
Сообщений в мини-форумах: 17

 

Вопрос # 1 000

Приветствую, уважаемые эксперты! Скажите, как можно реализовать некоторое подобие рабочего стола, то есть расположить иконку на форме, при щелчке на которую (иконку) будет открываться соответствующий файл?

Вопрос задал: alone (статус: Посетитель)
Вопрос отправлен: 24 октября 2007, 16:02
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Вадим К

Здравствуйте, Гадлевский Олег Вячеславович!
Делаем так.
На форму кладём ListView
выставляем свойство
IconOptions.Arrangement = iaLeft
тепер иконки будут выстраиваться как положенно на рабочем столе, тоесть сверху вниз
ставимь на форму ImageList, куда грузим нужные иконки. у него выставляем свойства Width и Height равными 32. или такими, какими нужны иконки. связываем с ListView через свойство LargeImage.
Добаляем в листвью иконки, приминив редактор, который можно вызвать, кликнув правой кнопкой мыши по ListView (Items Editor)
можно выставить цвет ListView, что бы было реалистичней.
выставить свойсво HotTrack = true можно симитировать режим "в один клик". тоесть, наводишь мышку на элемент, а он выделяется. HotTrackStyle позволит более точно сделать подобие.
также неплохо выставить MultiSelect
Теперь сделаем запуск по двойному клику. для этого нужно создать обработчик события OnDblClick
там напишем такое

if ListView1.ItemIndex = -1 then
  exit;
ShowMessage(ListView1.Items[ListView1.ItemIndex].Caption);
Теперь при двойному клику будет появлятся сообщение с именем элемента. Индекс его тоже известен.

А вот что на самом деле должно отображаться - это решать вам.
Теперь о том, как "запустить" файл. для этого добавим в список Uses юнит ShellApi и будем использовать такой код
ShellExecute (Form1.Handle, nil, PAnsiChar(filename), nil, nil, SW_RESTORE);
в качестве filename может быть как имя выполнимого файла так и например вордовский документ.
а вот такой строкой можно запустить броузер
ShellExecute (Form1.Handle, nil, 'iexplore', 'http://expert.delphi.int.ru', nil, SW_RESTORE);

Остался последний вопрос. "а как связать иконку и путь к файлу (действие)? ведь неудобно по имени делать выбор".
Тут советую прочитать мою статью о виртуальном ListView
Как прицепить всплывающее меню предлагаю подумать самостоятельно.

Ответ отправил: Вадим К (статус: Доктор наук)
Ответ отправлен: 24 октября 2007, 16:46
Оценка за ответ: 5
Комментарий: Спасибо большое!


Вопрос # 1 001

Здравствуйте, уважаемые эксперты! Как стройть графики с помощью TChart.

Вопрос задал: Leonardo (статус: Посетитель)
Вопрос отправлен: 24 октября 2007, 16:37
Всего ответов: 1; сообщений в мини-форуме вопроса: 1

 

Ответ #1. Отвечает эксперт: min@y™

В папке %Delphi%DemosTeeChart лежит офигенное демо по построению всевозможных графиков, диаграмм и пр. Файл проекта - teedemo.dpr, скомпили его и запусти ЕХЕ-шник, посмотри. Выбери понравившийся график и изучи исходник его построения.

Ответ отправил: min@y™ (статус: Магистр)
Ответ отправлен: 25 октября 2007, 08:19


Вопрос # 1 002

Здравствуйте! Скажите пожалуйста, как добавить иконку в ListView?

Вопрос задал: alone (статус: Посетитель)
Вопрос отправлен: 25 октября 2007, 10:16
Всего ответов: 1; сообщений в мини-форуме вопроса: 3

 

Ответ #1. Отвечает эксперт: min@y™

Пишу по памяти. Берёшь ImageList и заполняешь его иконками, какими надо. Затем ставишь у ListView свойства Images и LargeImages равными твоему ImageList.

ListView.ViewStyle:= vsIcon;
 
with ListView.Items.Add() do
  begin
    Caption:= 'Название новой иконки';
    ImageIndex:= <номер иконки в ImageList>;
  end;

Ну вот как-то так.

Ответ отправил: min@y™ (статус: Магистр)
Ответ отправлен: 25 октября 2007, 15:22
Оценка за ответ: 5
Комментарий: еще бы знать, как заполнить ImageList иконками


Вопрос # 1 004

Здравствуйте, уважаемые эксперты!
Как скопировать текст из "мемо" в буфер обмена винды?
И как вставить? Спасибо.

Вопрос задал: Anton-L (статус: Посетитель)
Вопрос отправлен: 25 октября 2007, 21:46
Всего ответов: 3

 

Ответ #1. Отвечает эксперт: Матвеев Игорь Владимирович

Здравствуйте, Anton-L!
1. Копирование текста:

Clipboard.SetTextBuf(PChar(Memo1.Text));

2. Вставка текста:
Memo1.Text := Clipboard.AsText;

Не забудьте включить в uses модуль Clipbrd в котором реализован класс TClipboard.

Ответ отправил: Матвеев Игорь Владимирович (статус: Студент)
Ответ отправлен: 26 октября 2007, 05:44
Оценка за ответ: 5

Ответ #2. Отвечает эксперт: min@y™

У TMemo есть также такие методы как CopyToClipboard(), CutToClipboard(), PasteFromClipboard(), которые копируют/вырезают выделенный текст из БО и вставляют из БО соответственно.

Ответ отправил: min@y™ (статус: Магистр)
Ответ отправлен: 26 октября 2007, 08:16
Оценка за ответ: 5

Ответ #3. Отвечает эксперт: Feniks

Здравствуйте, Anton-L!
Дополнение к выше изложенным ответам.
Программная реализация Cut, Copy и Paste:

procedure TForm1.Cut1Click(Sender: TObject);
begin
   SendMessage (ActiveControl.Handle, WM_Cut, 0, 0);
end;
 
procedure TForm1.Copy1Click(Sender: TObject);
begin
   SendMessage (ActiveControl.Handle, WM_Copy, 0, 0);
end;
 
procedure TForm1.Paste1Click(Sender: TObject);
begin
   SendMessage (ActiveControl.Handle, WM_Paste, 0, 0);
end;

Ответ отправил: Feniks (статус: Бакалавр)
Ответ отправлен: 26 октября 2007, 14:17
Оценка за ответ: 5


Вопрос # 1 005

Здравствуйте!
Ответь мне пожалуйста. Как мне запретить установки любых программ в Windows XP?
C уважением Мехродж.

Вопрос задал: POWER (статус: Посетитель)
Вопрос отправлен: 25 октября 2007, 22:01
Всего ответов: 2

 

Ответ #1. Отвечает эксперт: Матвеев Игорь Владимирович

Здравствуйте, POWER!
Любых не получится, т.к. каждый инсталлятор устанавливает по-разному. Кроме того, множество программных продуктов инсталлируются собственными инсталляторами, поэтому "отловить" их просто невозможно.

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

Ответ отправил: Матвеев Игорь Владимирович (статус: Студент)
Ответ отправлен: 26 октября 2007, 05:50

Ответ #2. Отвечает эксперт: Косолапов Дмитрий Юрьевич

Здравствуйте, POWER!
По всей видимости, надо просто в правах пользователя (если речь идет о Win2k/XP/Vista) запретить установку программ. Делать для этого программу на Дельфи вряд ли целесообразно.

Ответ отправил: Косолапов Дмитрий Юрьевич (статус: 8-ой класс)
Ответ отправлен: 26 октября 2007, 10:00


Вопрос # 1 006

Доброго времени суток, уважаемые эксперты!

Можно ли работать в потоке с классом TBitmap без использования Synchronize?

Поясню на всякий случай: отрисовка на Bitmap идёт внутри потока, потом через Synchronize вызывается метод главной формы, который рисует этот Bitmap.
При такой реализации через некоротое время программа выдаёт исключение EOutOfResource, хотя никаких потерь памяти не происходит.

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

Задаю вопрос в этот раздел, т.к. он касается работы VCL и TThread.

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

Вопрос задал: Soulnet (статус: Посетитель)
Вопрос отправлен: 25 октября 2007, 22:03
Всего ответов: 1; сообщений в мини-форуме вопроса: 1

 

Ответ #1. Отвечает эксперт: Вадим К

Здравствуйте, Soulnet!
Если в теле потока происходит отрисовка на TBitmap, который пренадлежит потоку, то проблем с синхнронизацией не должно быть. Потому, что TBitmap не есть оконным элементом.
Сообщение EOutOfResource не означает, что у вас происходят утечки памяти. Это утечки ресурсов. Дело в том, что каждое окно в системе (кнопка в понятии windows - окно, а label - нет, это просто нарисованно на форме) требует одного хендла. А Форма требует где то 4-5 (на само окно, на pen и brush для Canvas, на Font). Это не было бы проблемой, если бы кол-во хендлов на процесс не было бы ограничено в 10000. (именно по этому одна программа не может создать больше где то 1300 форм одновременно).
У вас наверно при отрисовке где то не освобождается хендл, а так как отрисовка идёт часто (так я понял), то они и кончаются. Нужно искать, где вы утеряли. А для этого, нужно смотреть код.

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

Ответ отправил: Вадим К (статус: Доктор наук)
Ответ отправлен: 26 октября 2007, 11:57
Оценка за ответ: 5


Вопрос # 1 007

Здравствуйте! Подскажите пожалуйста, можно ли в ListView сделать фон картинкой а не просто цветом?

Вопрос задал: alone (статус: Посетитель)
Вопрос отправлен: 25 октября 2007, 22:30
Всего ответов: 1; сообщений в мини-форуме вопроса: 3

 

Ответ #1. Отвечает эксперт: min@y™

Глянь для начала сюда, сюда и вот сюда.

Ответ отправил: min@y™ (статус: Магистр)
Ответ отправлен: 26 октября 2007, 08:35
Оценка за ответ: 5
Комментарий: Спасибо, этой информации вполне достаточно


Вопрос # 1 008

Здравствуйте, эксперты!

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

Вопрос задал: Emfs (статус: Посетитель)
Вопрос отправлен: 26 октября 2007, 13:52
Всего ответов: 2

 

Ответ #1. Отвечает эксперт: Вадим К

Здравствуйте, Emfs!
Есть три способа сделать это.
1) серверный. Плагины - длл, в которых есть заведомо известные функции. Например функция с именем PluginName - которая возвращает имя плагина. Само приложение создаёт для них пункт меню и вызывает по мере надобности.
2) клиентский. Плагин имеет только несколько функций. а то и одну вообще. Этой функцией передаётся ссылка на функцию в главном приложении, которая имеет где то такой вид
function Work(TypeOfJob:integer; param1, param2:integer);
где первый параметр - это код действия, второй и третий - параметры. Теперь плагин сам прописывает себе пункты меню, перехватывает действия.
3) смешанный, самый распространённый. Включает в себя оба предыдущих.

Миранда использует 2 способ. каждый плагин имеет 4 функции.
Load и Unload - загрузить и выгрузить плагин. при загрузке плагину передаётся структура с функциями и данными главного приложения (например, интерфейс для контактлиста).
MirandaPluginInfoEx - плагин выдает структуру, в которой есть его описание(автор, функции)
MirandaPluginInterfaces - плагин выдаёт интерфес для управления им.

Теперь два слова о том, как может быть реализовано всплывающее меню.
Главное приложение когда нужно отобразить меню, просматривает список плагинов. И проверят, какие могут добавить пункты меню(информацию о себе плагины дали в MirandaPluginInfoEx). Дальше у каждого запрашивается массив записей вида "пункт меню"-"Id". Если пункт меню был выбран, то вызывается функция плагина (она будет получена через MirandaPluginInterfaces) для обработки меню и передаётся id. Id - какое то уникальное число-идентификатор или например строка.
----
почитайте также здесь
http://www.delphikingdom.com/asp/viewitem.asp?catalogid=274
http://www.uil.net/uArticles/ImplementingPluginsforyou.html

Ответ отправил: Вадим К (статус: Доктор наук)
Ответ отправлен: 26 октября 2007, 14:14

Ответ #2. Отвечает эксперт: min@y™

В кратце: плагины делаются в виде DLL, разработчикам этих DLL предоставляется API для взаимодействия с основной программой, т.е. имена и типы параметров экспортируемых и callback-функций (заголовочные файлы). Все DLL подключаются динамически. Советую посмотреть в инете, как пишутся плагины для Far'a и/или Total Commander'a. Я один такой когда-то написал.

Вот ссылки нагуглил тебе:


  1. Написание плагина для FAR на Delphi (ID3 Viewer);
  2. Сайт, посвящённый Total Commander'у.


Для начала, я думаю, хватит.

Ответ отправил: min@y™ (статус: Магистр)
Ответ отправлен: 26 октября 2007, 14:17


Вопрос # 1 009

21. Как сделать циклы while и repeat бесконечными, и как в этом случае можно выйти из цикла.

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 26 октября 2007, 22:17
Всего ответов: 3; сообщений в мини-форуме вопроса: 1

 

Ответ #1. Отвечает эксперт: Косолапов Дмитрий Юрьевич

Здравствуйте, natasha!
while true do ... ;
repeat ... until false;

Выход из цикла - break.

Ответ отправил: Косолапов Дмитрий Юрьевич (статус: 8-ой класс)
Ответ отправлен: 26 октября 2007, 22:57

Ответ #2. Отвечает эксперт: Ершов Денис

Здравствуйте, natasha!
К предыдущему ответу могу добавить, что выход из цикла произойдет также в случае встречи команд exit и halt.

Ответ отправил: Ершов Денис (статус: 6-ой класс)
Ответ отправлен: 27 октября 2007, 16:58

Ответ #3. Отвечает эксперт: min@y™

Добавлю к предыдущим ответам.
Быстрый выход из глубоко вложенных циклов удобно делать генерацией исключения raise exception.create().

Ответ отправил: min@y™ (статус: Магистр)
Ответ отправлен: 28 октября 2007, 08:29


Вопрос # 1 010

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

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 26 октября 2007, 22:22
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Матвеев Игорь Владимирович

Здравствуйте, natasha!

function UpRus(input: string): string;
var
  i, l : DWord;
  c    : Char;
begin
 l := Length(input);
 Result := '';
 for i := 1 to l do
   begin
     if input[i] in ['а'..'я'] then c := Chr( Ord(input[i]) - 32 )
       else c := input[i];
     Result := Result + c;
   end;
end;
 
function DownRus(input: string): string;
var
  i, l : DWord;
  c    : Char;
begin
 l := Length(input);
 Result := '';
 for i := 1 to l do
   begin
     if input[i] in ['А'..'Я'] then c := Chr( Ord(input[i]) + 32 )
       else c := input[i];
     Result := Result + c;
   end;
end;

Пример обращения:
Edit1.Text := UpRus(Edit1.Text);
Edit2.Text := DownRus(Edit2.Text);

Ответ отправил: Матвеев Игорь Владимирович (статус: Студент)
Ответ отправлен: 27 октября 2007, 05:00


Вопрос # 1 012

Доброго времени суток, уважаемые эксперты!

Как применяется функция Random я знаю. Интересно, можно ли заполнить с ее помощью массив положительными и отрицательными числами, но чтобы не было нулей.

Вопрос задал: puporev (статус: Посетитель)
Вопрос отправлен: 27 октября 2007, 07:57
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Dron

Здравствуйте, puporev!
Конечно можно! Имея такой простой инструмент, как генератор случайных чисел, можно построить алгоритмы любой сложности подборки случайных чисел. А теперь конкретно, как заполнить массив положительными и отрицательными числами, но чтобы не было нулей.
Для удобства можно написать миниатюрную функцию, которая возвращала бы случайное число из указанного диапазона:

function MyRandom(Min, Max: Integer): Integer;
begin
  Result:=Random(Max-Min+1)+Min;
end;
Принцип работы очень прост - сначала мы выбираем случайное число от 0 до длины заданного диапазона, а затем делаем смещение, равное нижней границе.
Дальше дело за малым - применить эту функцию. В примере генерируются 20 чисел от -10 до 20 и выводятся в ListBox:
procedure TForm1.Button1Click(Sender: TObject);
label NewRand;
var i,r,min_r,max_r: integer;
begin
  min_r:=-10; {Нижняя граница}
  max_r:=20; {Верхняя граница}
  for i := 1 to 20 do
  begin
    NewRand:
    r:=MyRandom(min_r,max_r);
    if r = 0 then goto NewRand;
    ListBox1.Items.Add(IntToStr(r))
  end
end;
Обратите внимание, что для отсеивания нулей мы просто "крутим" генератор до тех пор, пока он не сгенерирует число, отличное от нуля. То же самое можно сделать без использования меток - с помощью цикла Repeat.
P.S. Не забудьте прописать вызов Randomize.
Удачи!

Ответ отправил: Dron (статус: Студент)
Ответ отправлен: 27 октября 2007, 09:23
Оценка за ответ: 5
Комментарий: Большое спасибо.
Исключить ноль программно я не подумал.


Вопрос # 1 013

Здравствуйте, эксперты! помогите Написать программу вычисления с заданной точностью корня квадратного из числа “a”, используя формулу Ньютона для последовательных приближений xnew = xold – (x2 – a)/(2a)

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 27 октября 2007, 11:28
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Николай Рубан

Здравствуйте, natasha!

Вам достаточно использовать такой алгоритм:

var x1,x2,eps:real;
function f(x:real):real;
begin
f:={Описание функции};
end;
function df(x:real):real;
begin
df:={Описание производной функции};
end;

begin
Write('Введите первое приближение x(0)');
Readln(x1);
Write('Введите точность');
Readln(eps);
x2:=maxint;
while abs(f(x2))>eps do
begin
x2:=x1;
writeln(x1);
readln;
x1:=x1-f(x1)/df(x1);
end;
end.

Good Luck!!!

Ответ отправил: Николай Рубан (статус: 10-ый класс)
Ответ отправлен: 27 октября 2007, 12:25


Вопрос # 1 014

Здравствуйте, эксперты! помогите Написать подпрограмму для вычисления с заданной точностью суммы бесконечного ряда 1 - x + x2/2! - x3 /3! + x4 /4! - …, для x<0

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 27 октября 2007, 11:30
Всего ответов: 2; сообщений в мини-форуме вопроса: 2

 

Ответ #1. Отвечает эксперт: Dron

Здравствуйте, natasha!
Сначала напишем функцию, вычисляющую n-ый член ряда по заданному x и n:

function GetElement(N: Integer; X: Real): Real;
begin
  if N = 1 then
    Result:=1
  else
    Result:=Power(X,N-1)/Factorial(N-1);
end;
Для вычисления факториала воспользуемся такой функцией:
function Factorial(X: Integer): LongInt;
var i: integer;
begin
  Result:=1;
  for i := 2 to X do
    Result:=Result*i;
end;
Использованная в примере функция Power находится в модуле Math, который нужно подключить, добавив его название в раздел uses.
А далее вспоминаем курс математического анализа - остаток ряда начиная с n-го члена не больше самого n-го члена. Этим и пользуемся:
procedure TForm1.Button1Click(Sender: TObject);
var S,X,A,E: Real; N: Integer;
begin
  X:=StrToFloat(Edit1.Text); {Число X}
  E:=StrToFloat(Edit2.Text); {Точность вычислений}
  N:=1;
  A:=GetElement(1,X);
  repeat
    S:=S+A;
    Inc(N);
    A:=GetElement(N,X);
  until A < E;
  Label1.Caption:=FloatToStr(S);
end;
В данном примере значение x вводится в Edit1, в Edit2 вводится точность вычисления (например, 0.0001). Полученная сумма выводится в Label1.
Ну и наконец, проверить точность достаточно просто - данный ряд является разложением функции ex, т.е. если взять x = 1, то сумма ряда будет равна самому числу e.
---
Добавлено: ряд, рассмотренный в примере, является рядом функции ex. Ряд, указанный в вопросе - знакопеременный, он рядом этой функции не является. Подробности в мини-форуме вопроса.

Ответ отправил: Dron (статус: Студент)
Ответ отправлен: 27 октября 2007, 11:59

Ответ #2. Отвечает эксперт: Николай Рубан

Здравствуйте, natasha!

Обращаю Ваше внимание на то, что в предыдущем ответе допущена неточность сумма находится по такому ряду:
1 + x + x2/2! + x3 /3! + x4 /4! + …
хотя по условию рад знакопеременный!!!
В результате сумма будет находиться не верно!

Вот привожу свой вариант решения.
На форме рекомендую расположить два объекта - ListBox1: TListBox и Button1: TButton;

Процедура расчета суммы при нажатии на кнопку:
procedure TForm1.Button1Click(Sender: TObject);
var s,x,xn,eps:real;
i:integer;
f:longint;
begin
eps:=0.0001; //точность вычислений
x:=1; //начальное значение х

s:=1;
xn:=x; f:=1; i:=1;
while abs(xn/f) > eps do
begin
s:=s+(-ord(odd(i))+ord(not odd(i)))*xn/f;
ListBox1.Items.Add(Format('an=%.5f xn=%.2f f=%d',[xn/f,xn,f])); //выводим промежуточные вычисления
xn:=xn*x;
inc(i); f:=f*i;
end;
ListBox1.Items.Add(Format('%.5f',[s])); //выводим непосредственно найденную сумму
end;

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

Good Luck!!!

Приложение:

Ответ отправил: Николай Рубан (статус: 10-ый класс)
Ответ отправлен: 27 октября 2007, 13:18


Вопрос # 1 016

Приветствую, уважаемые эксперты! 47. Написать подпрограмму для вычисления с заданной точностью суммы бесконечного ряда 1 + w + w2 + w3 + w4 + …, для w<0
48. Написать подпрограмму для вычисления с заданной точностью суммы бесконечного ряда 1 - x + x2/2 - x3 /3 + x4 /4 - …, для x<0

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 27 октября 2007, 11:44
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Dron

Здравствуйте, natasha!
См. мой ответ на вопрос N 1014. С этими рядами всё делается точно также, только нужно изменить функцию GetElement. В первом случае:

Result:=Power(X,N-1);
; во втором - точно также, как в вопросе 1014, только убрать факториал.

Ответ отправил: Dron (статус: Студент)
Ответ отправлен: 27 октября 2007, 12:07


Вопрос # 1 017

Приветствую, уважаемые эксперты! 45. Написать подпрограмму, которая бы уменьшала аргумент тригонометрических функций до значения меньшего 2pi.
46. Написать подпрограмму удаления всех вхождений подстроки в строку, используя стандартные функции Pos и Delete.

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 27 октября 2007, 11:47
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Николай Рубан

Здравствуйте, natasha!

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

function DelSubstr(sub,s:string):string;
var i,t:integer;
begin
t:=1;
i:=pos(sub,s);
while i>0 do
begin
t:=t+i-1;
delete(s,t,length(sub));
i:=pos(sub,copy(s,t,length(s)));
end;
DelSubstr:=s;
end;

Использование написанной функции:
var s,sb:string;
begin
...
s:='poposposs';
sb:='pos';
ShowMessage(format('Исходная строка="%s"'+#13+'Подстрока="%s"'#13+
'Новая строка="%s"',[s,sb,DelSubstr(sb,s)]));
...
end.

Good Luck!!!

Ответ отправил: Николай Рубан (статус: 10-ый класс)
Ответ отправлен: 28 октября 2007, 01:18


Вопрос # 1 018

Приветствую, уважаемые эксперты!
41. Написать функцию вычисления семы чисел натурального ряда от 1 до N.
42. Написать функцию для возведения вещественного числа в целую степень.

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 27 октября 2007, 11:51
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Николай Рубан

Здравствуйте, natasha!

Функция вычисления суммы чисел натурального ряда от 1 до N.
function natSum(n:integer):longint;
var i:integer;
begin
Result:=0;
for i:=1 to n do Result:=Result+i;
end;


Функция для возведения вещественного числа в целую степень.
function NewPower(x:real; n:integer):extended;
var i:integer;
begin
Result:=x;
for i:=1 to n-1 do Result:=Result*x;
end;

Применение выше описанных функций:

ShowMessage(format('Сумма=%d',[natSum(6)]));
ShowMessage(format('Степень=%.5f',[NewPower(2,10)]));


Good Luck!!!

Ответ отправил: Николай Рубан (статус: 10-ый класс)
Ответ отправлен: 27 октября 2007, 14:47


Вопрос # 1 019

39. Написать функцию вычисления n!.
40. Написать функцию вычисления n–ного числа Фибоначчи.

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 27 октября 2007, 11:59
Всего ответов: 2

 

Ответ #1. Отвечает эксперт: Dron

Здравствуйте, natasha!
Функцию вычисления факториала можно написать с использованием цикла, либо с использованием рекурсии. Рекурсию лучше не использовать, т.к. в этом случае расход памяти будет гораздо больше, причём чем больше будет аргумент, тем больше выделение памяти. Вот реализация функции с использованием цикла:

function Factorial(N: Integer): LongInt;
var i: integer;
begin
  Result:=1;
  for i := 2 to N do
    Result:=Result*i;
end;

Ответ отправил: Dron (статус: Студент)
Ответ отправлен: 27 октября 2007, 12:16

Ответ #2. Отвечает эксперт: Николай Рубан

Здравствуйте, natasha!

Функция вычисления n!.
function fact(n:integer):longint;
var i:integer;
begin
Result:=1;
if n>0 then
for i:=2 to n do Result:=Result*i;
end;


Функция вычисления n–ного числа Фибоначчи.
function Fibonachi(n:integer):integer;
var f1,f2,i:integer;
begin
f1:=1; f2:=1;
Result:=1;
if n>2 then
for i:=3 to n do
begin
Result:=f1+f2;
f1:=f2;
f2:=Result;
end;
end;


Применение выше описанных функций:
ShowMessage(format('Фактириал=%d',[fact(6)]));
ShowMessage(format('Фибоначчи=%d',[Fibonachi(13)]));

Good Luck!!!

Ответ отправил: Николай Рубан (статус: 10-ый класс)
Ответ отправлен: 27 октября 2007, 15:01


Вопрос # 1 020

37. Написать функцию, которая возвращает среднее арифметическое для N случайных чисел.
38. Написать процедуру, которая возвращает максимальное и минимальное значения в последовательности N случайных чисел.

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 27 октября 2007, 12:02
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Николай Рубан

Здравствуйте, natasha!

Функция, которая возвращает среднее арифметическое для N случайных чисел.
function SArifmetic(n:integer):real;
var i:integer;
begin
randomize;
Result:=0;
if n>0 then
begin
for i:=1 to n do Result:=Result+random(100);
Result:=Result/n;
end;
end;

Процедура, которая возвращает максимальное и минимальное значения в последовательности N случайных чисел.
procedure RandomMinMax(n:integer;var mn,mx:integer);
var i,t:integer;
begin
randomize;
mx:=random(100);
mn:=mx;
for i:=2 to n do
begin
t:=random(100);
if t > mx then mx:=t;
if t < mn then mn:=t;
end;
end;

Применение выше описанных функций:
var min,max:integer;
...
ShowMessage(format('Среднее арифметическое=%.3f',[SArifmetic(7)]));
RandomMinMax(10,min,max);
ShowMessage(format('Минимальное=%d Максимальное=%d',[min,max]));
...

Good Luck!!!

Ответ отправил: Николай Рубан (статус: 10-ый класс)
Ответ отправлен: 27 октября 2007, 15:25


Вопрос # 1 021

35. Написать функцию, которая возвращает максимальное число из последовательности N случайных чисел.
36. Написать функцию, которая возвращает сумму N случайных чисел.

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 27 октября 2007, 12:07
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Николай Рубан

Здравствуйте, natasha!

Функция, которая возвращает максимальное число из последовательности N случайных чисел.
function RandomMax(n:integer):integer;
var i,t:integer;
begin
randomize;
Result:=random(100);
for i:=2 to n do
begin
t:=random(100);
if t > Result then Result:=t;
end;
end;



Функция, которая возвращает сумму N случайных чисел.
function RandomSum(n:integer):longint;
var i:integer;
begin
randomize;
Result:=0;
for i:=1 to n do Result:=Result+random(100);
end;


Применение выше описанных функций:
ShowMessage(format('Случайная сумма из N чисел=%d',[RandomSum(10)]));
ShowMessage(format('Максимальное число из последовательности N чисел=%d',[RandomMax(10)]));

Good Luck!!!

Ответ отправил: Николай Рубан (статус: 10-ый класс)
Ответ отправлен: 27 октября 2007, 15:34


Вопрос # 1 023

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

Вопрос задал: alone (статус: Посетитель)
Вопрос отправлен: 27 октября 2007, 19:18
Всего ответов: 2

 

Ответ #1. Отвечает эксперт: Николай Рубан

Здравствуйте, Гадлевский Олег Вячеславович!

Вам достаточно в Object Inspector-e в свойстве BorderStyle формы установить параметр bsToolWindow.

И создать обработчик FormCreate(Sender: TObject) в него прописать такую строку:
procedure TForm1.FormCreate(Sender: TObject);
begin
WindowState:=wsMaximized;
end;

ЗАМЕЧАНИЕ: Не следует свойство WindowState также как и BorderStyle изменять вручную в Object Inspector-e поскольку Вы НЕ добьетесь нужного результата.

Good Luck!!!

Ответ отправил: Николай Рубан (статус: 10-ый класс)
Ответ отправлен: 27 октября 2007, 20:19
Оценка за ответ: 5
Комментарий: Спасибо

Ответ #2. Отвечает эксперт: Ершов Денис

Здравствуйте, Гадлевский Олег Вячеславович!
Как-то приходилось решать подобную задачу для устройства с терминальным управлением. Установил свойства BorderStyle в bsNone, а WindowState в wsMaximized. Если вам не нужен системный заголовок окна, то это ваш вариант.

Ответ отправил: Ершов Денис (статус: 6-ой класс)
Ответ отправлен: 28 октября 2007, 19:42
Оценка за ответ: 5


Вопрос # 1 024

Здравствуйте, уважаемые эксперты!
Мне необходимо в своём приложении делать интерполяцию графика построенного в Tchart. Delphi я занимаюсь недавно,поэтому не знаю, может метод интерполяции заложен в Tchart, а если нет помогите тогда с кодом.

Вопрос задал: GAZ (статус: Посетитель)
Вопрос отправлен: 28 октября 2007, 08:45
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Матвеев Игорь Владимирович

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

В книге "Delphi5 Создание мультимедийных приложений" Н. Тюкачев, Ю. Свиридов, есть описание нескольких алгоритмов интерполяции: Многочлен Лагранжа, Метод наименьших квадратов, Сплайны и Кривые Безье.

Отрывок из книги, а также листинги см. в приложении.
К ответу прикреплён файл. Загрузить » (срок хранения: 60 дней с момента отправки ответа)

Ответ отправил: Матвеев Игорь Владимирович (статус: Студент)
Ответ отправлен: 29 октября 2007, 08:35
Оценка за ответ: 5
Комментарий: Спасибо за прикреплённый файл


Вопрос # 1 025

Доброго времени суток, уважаемые эксперты!
Мне нужно прочитать файл с данными, записанными восьмиричной кодировкой, перевести эти данные в нормальный вид и записать в другой файл. Например, в текстовой. Как это сделать? Спасибо.

Вопрос задала: Тамара (статус: Посетитель)
Вопрос отправлен: 28 октября 2007, 11:25
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: min@y™

Не знаю, в каком виде хранятся данные у тебя во входном файле, но это не принципиально. Вот функция перевода числа S системы счисления с основанием B в целое (пёрто из RxStrUtils.pas):

function Numb2Dec(S: string; B: Byte): Longint;
var
  I, P: Longint;
begin
  I := Length(S);
  Result := 0;
  S := UpperCase(S);
  P := 1;
  while (I >= 1) do begin
    if S[I] > '@' then Result := Result + (Ord(S[I]) - 55) * P
    else Result := Result + (Ord(S[I]) - 48) * P;
    Dec(I);
    P := P * B;
  end;
end;

Как записать числа в файл, я думаю труда не составит (?).

Ответ отправил: min@y™ (статус: Магистр)
Ответ отправлен: 29 октября 2007, 13:22
Оценка за ответ: 5
Комментарий: Здорово! Записать смогу.


Вопрос # 1 026

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

Вопрос задала: Тамара (статус: Посетитель)
Вопрос отправлен: 28 октября 2007, 11:31
Всего ответов: 3

 

Ответ #1. Отвечает эксперт: Dron

Здравствуйте, Тамара!
Я думаю, что в данном случае неприменимо сравнивать системы создания инсталляторов по отношению к Delphi-программам. Любой инсталлятор может работать с любыми файлами, независимо от того, где и как они были созданы.
По поводу инсталляторов... Лично я рекомендую Inno Setup. Очень давно им пользуюсь и никаких нареканий нет. Функциональность очень большая, сделать можно всё, что угодно. Достаточно просто разобраться - не требует каких-либо специальных знаний. Периодически появляются новые версии. Официальный сайт: http://jrsoftware.org/isinfo.php
Желаю удачи!

Ответ отправил: Dron (статус: Студент)
Ответ отправлен: 28 октября 2007, 12:20
Оценка за ответ: 5
Комментарий: Спасибо за ответ. Учту.

Ответ #2. Отвечает эксперт: Вадим К

Здравствуйте, Тамара!
Я долго пользуюсь NSIS. Под него есть много плагинов и примеров. Также есть редактор, который может в режиме мастера создать готовый скрипт для инсталяции. Основное приимущество, благодаря которому я его выбрал - это то, что создав скрипт (специальный текстовый файл с указаниями для инсталлятора) можно даже с коммандной строки запустить и получить готовый инсталлятор.
Скачать можно здесь http://nsis.sourceforge.net/Main_Page
также рекомендую посетить русскоязычный сайт - http://nsis.narod.ru/
Здесь http://hmne.sourceforge.net/ скачать простенький, но очень неплохой редактор.
Ну и сюда неплохо заглянуть http://ru.wikipedia.org/wiki/Nullsoft_Scriptable_Install_System

Ответ отправил: Вадим К (статус: Доктор наук)
Ответ отправлен: 28 октября 2007, 13:37
Оценка за ответ: 5
Комментарий: Спасибо, посмотрю ссылки.

Ответ #3. Отвечает эксперт: Ершов Денис

Здравствуйте, Тамара!
C Delphi поставляется довольно мощная система создания инсталлеров InstallShild Express Borland Limited Edition. Как видно из названия система специально заточена под Delphi. Имеются средства для импорта модулей, например драйверов баз данных. Лично мне функций программы хватало с головой.

Ответ отправил: Ершов Денис (статус: 6-ой класс)
Ответ отправлен: 1 ноября 2007, 18:25
Оценка за ответ: 5
Комментарий: С моим Delphi он не поставлялся, поэтому вопрос и возник.


Вопрос # 1 027

Приветствую, уважаемые эксперты!
10. Найти значения переменных “х” и “у” после выполнения приведенного фрагмента программы, при различных значениях “a” и “b”.
x:=0; y:=0;
if a>b then
x:=1; y:=2;

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 28 октября 2007, 18:08
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Иусов Сергей Ник.

Здравствуйте, natasha!
Нечнем с значения Y. Оно будет равно 2 не зависимо от значений A и B.
С X есть 2 варианта:
1. При A>B - X будет равнятся 1
2. В остальных случаях 0

Ответ отправил: Иусов Сергей Ник. (статус: 3-ий класс)
Ответ отправлен: 28 октября 2007, 18:15
Оценка за ответ: 5


Вопрос # 1 028

Приветствую, уважаемые эксперты!
11. Найти значения переменных “х” и “у” после выполнения приведенного фрагмента программы, при различных значениях переменных“a” и “b”.
x:=0; y:=0;
if a<>b then x:=1
else x:=2; y:=3;

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 28 октября 2007, 18:28
Всего ответов: 2

 

Ответ #1. Отвечает эксперт: Николай Рубан

Здравствуйте, natasha!

Все очевидно:
Изначально x=0; y=0;
В зависимости от того переменная "a" равна или нет переменной "b" получаем два случая:
если a=b, то x=2 и y:=3;
если a<>b, то x=1 и y:=3;

Получили два ответа, как видно значение переменнай "у" НЕ зависит от значений "a" и "b".

Good Luck!!!

Ответ отправил: Николай Рубан (статус: 10-ый класс)
Ответ отправлен: 28 октября 2007, 18:42
Оценка за ответ: 5

Ответ #2. Отвечает эксперт: Иусов Сергей Ник.

Здравствуйте, natasha!
Здесь, так же как и в предыдущем вопросе, значение Y не зависит от значений A и B. И после выполнения данного кода Y будет равен трем. Значение X при A=B будет равно 2, в остальных случаях X = 1.

Ответ отправил: Иусов Сергей Ник. (статус: 3-ий класс)
Ответ отправлен: 28 октября 2007, 18:43
Оценка за ответ: 5


Вопрос # 1 029

Приветствую, уважаемые эксперты!
12. Какой результат получится при выполнении приведенного фрагмента программы, при различных значениях переменных “a” и “b”.
x:=0; y:=0;
if a<>b then x:=1; y:=2;
else x:=2; y:=3;

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 28 октября 2007, 18:34
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Иусов Сергей Ник.

Здравствуйте, natasha!
Приведенный код не скомпилируется. Ошибка в операторе if .. then .. else

Ответ отправил: Иусов Сергей Ник. (статус: 3-ий класс)
Ответ отправлен: 28 октября 2007, 18:46
Оценка за ответ: 5


Вопрос # 1 030

Приветствую, уважаемые эксперты!
13. Найти значения переменной “х” после выполнения приведенного фрагмента программы, при различных значениях переменной «i».
var i:integer;

x:=1;
case i of
1: x:=10*i;
2: x:=20*i;
else x:=100;
end;

Вопрос задала: natasha (статус: Посетитель)
Вопрос отправлен: 28 октября 2007, 18:36
Всего ответов: 1

 

Ответ #1. Отвечает эксперт: Иусов Сергей Ник.

Здравствуйте, natasha!
При i = 1 -> X = 10
При i = 2 -> X = 40
в остальных случаях X=100

Ответ отправил: Иусов Сергей Ник. (статус: 3-ий класс)
Ответ отправлен: 28 октября 2007, 18:48


Обучение Delphi

» Найти все предыдущие уроки можно на www.delphi.int.ru в разделе "Статьи". Последние 5 уроков:

21. Подпрограммы (часть 3)
22. Простые типы данных
23. Множества
24. Записи (часть 1)
25. Работа с файлами и каталогами (часть 1)

Записи (часть 2)

Автор:
© Ерёмин А.А., 2009
Если программа тебе понятна, значит, она уже устарела...
Номер урока:
26

Введение

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

Записи с вариантами

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

Описание отрезка

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

  1. Двумя точками, каждая из которых имеет координаты X и Y (т.е. X1,Y1,X2,Y2).
  2. Одной точкой (X,Y), длиной отрезка и углом между ним и какой-либо осью (например, осью X).

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

type
  TLineSegment = record
    X1,Y1: Real;
    X2,Y2: Real;
  end;

Для наглядности точки описаны отдельно, хотя короче будет поместить их в одну строку (X1,Y1,X2,Y2: Real).

Теперь второй вариант:

type
  TLineSegment = record
    X,Y: Real; //Один из концов отрезка
    Angle: Real; //Угол наклона
    Length: Real; //Длина отрезка
  end;

Всё хорошо, оба варианта рабочие и удобные... Но теперь представьте, что в программе мы должны предоставить пользователю возможность ввода отрезка и первым, и вторым способом, т.е. он сам будет решать, как ему удобнее. Что делать в этом случае? Не писать же 2 программы, базируясь то на одной структуре, то на другой? Вот тут-то нам и придут на помощь записи с вариантами.

Сначала давайте опишем простой перечислимый тип данных, который содержит два значения - тип описания отрезка:

type TLineSegmentType = (lsPoints,lsPolar);

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

TLineSegment = record
  LType: TLineSegmentType;
end;

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

TLineSegment = record
  case LType: TLineSegmentType of
    lsPoints:
      //Здесь нужно описать первый набор полей...
    lsPolar:
      //...а здесь второй
  end;
end;

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

TLineSegment = record
  case LType: TLineSegmentType of
    lsPoints: (
      X1,Y1: Real;
      X2,Y2: Real;
    );
    lsPolar: (
      X,Y: Real;
      Angle: Real;
      Length: Real;
    );
end;

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

В нашем случае структура всё ещё не оптимальна: координаты одной из точек у нас описаны в обоих наборах. Давайте вынесем их как постоянные поля:

type
  TLineSegmentType = (lsPoints,lsPolar);
 
  TLineSegment = record
    X,Y: Real; //Один из концов отрезка
    case LType: TLineSegmentType of
      lsPoints: (
        X2,Y2: Real; //Второй конец отрезка
      );
      lsPolar: (
        Angle: Real; //Угол наклона
        Length: Real; //Длина
      );
  end;

Это окончательный вид нашей записи. Посмотрите ещё раз и осмыслите написанное.

Ну а теперь перейдём к более знакомым вещам - сделаем интерфейс для ввода информации об отрезке и запрограммируем внесение всех данных в запись.

Форма ввода

Уверен, что с интерфейсной частью вы справитесь сами, поэтому привожу лишь код кнопки:

procedure TForm1.SaveButtonClick(Sender: TObject);
var L: TLineSegment;
begin
  if PointsRadio.Checked then
    L.LType:=lsPoints
  else
    L.LType:=lsPolar;
  case L.LType of
    lsPoints:
      begin
        L.X:=StrToFloat(X1Edit.Text);
        L.Y:=StrToFloat(Y1Edit.Text);
        L.X2:=StrToFloat(X2Edit.Text);
        L.Y2:=StrToFloat(Y2Edit.Text);
      end;
    lsPolar:
      begin
        L.X:=StrToFloat(XEdit.Text);
        L.Y:=StrToFloat(YEdit.Text);
        L.Angle:=StrToFloat(AngleEdit.Text);
        L.Length:=StrToFloat(LengthEdit.Text);
      end;
  end;
end;

Сначала мы смотрим, какой способа ввода у нас выбран на форме и соответствующим образом устанавливаем переменную-селектор LType. А затем уже переносим данные из полей ввода в нашу запись: если первый способ - из 4 левых полей, если второй - из 4 правых.

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

  1. По двум точкам: длина - это квадратный корень из суммы квадратов разностей соответствующих координат.
  2. По точке, углу и длине: промолчу, ага.
function GetLength(L: TLineSegment): Real;
begin
  case L.LType of
    lsPoints: Result:=Sqrt(Sqr(L.X2-L.X)+Sqr(L.Y2-L.Y));
    lsPolar: Result:=L.Length;
  end;
end;

Что может быть проще? Ну и в конец обработчика нажатия кнопки добавим:

MessageDlg('Длина отрезка: '+FloatToStr(GetLength(L)),mtInformation,[mbOk],0)

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

Упакованные записи

Пару слов о том, что такое упакованные записи, и с чем их едят. По умолчанию память под записи выделяется не очень экономно - помимо самих данных добавляются и служебные байты, которые отделяют блоки данных друг от друга. Существует принудительный способ заставить Delphi упаковать запись, т.е. минимизировать занимаемую ей память. Делается это указанием слова packed перед словом record. Разница порой может быть достаточно ощутимой. Пример: запись из строки длиной 5 символов, одного символа и трёх чисел разного типа. Объявим две разные записи: одна обычная, а другая упакованная:

TRecord1 = packed record
  Name: String[5];
  A: LongInt;
  C: Char;
  D: Double;
  N: Integer;
end;
 
TRecord2 = record
  Name: String[5];
  A: LongInt;
  C: Char;
  D: Double;
  N: Integer;
end;

А теперь самое интересное: посмотрим, сколько памяти занимает каждая из записей. Сделаем это функцией SizeOf():

var R1: TRecord1; R2: TRecord2;
begin
  ShowMessage(IntToStr(SizeOf(R1)));
  ShowMessage(IntToStr(SizeOf(R2)));
end;

В первом сообщении мы увидим 24, а во втором 32. Обычная запись занимает на треть больше памяти, чем упакованная! А теперь представьте, что у вас 100 000 таких записей?

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

Быстрый доступ к полям записей

В нашем примере у записи сравнительно мало полей - 4. Но бывают программы, где создаются записи с десятком полей и работа из-за этого замедляется. Хотя бы потому, что в коде приходится каждый раз набирать имя записи и точку, и лишь затем имя поля. Обрадую: мучаемся не только мы - компьютеру тоже приходится делать больше телодвижений. Каждый раз нужно определять адрес и искать запись в памяти, и лишь после этого можно найти значение поля. Чтобы облегчить участь и программисту и машине, был введён специальный оператор with (англ. "с"). К сожалению, о нём далеко не все знают, а ведь его использование увеличивает эффективность и кода, и работы самого программиста.

Итак, общая форма записи:

with запись do
 {обращение к полям записи}

Не очень понятно? А теперь на нашем примере:

with L do
begin
  X:=StrToFloat(X1Edit.Text);
  Y:=StrToFloat(Y1Edit.Text);
  X2:=StrToFloat(X2Edit.Text);
  Y2:=StrToFloat(Y2Edit.Text);
end;

Что же мы имеем? А вот что: мы нашу запись "вынесли за скобку", и далее напрямую обращаемся к её полям. Удобно, не правда ли? Этот код абсолютно эквивалентен тому, что был написан нами ранее, только он более эффективен.

Несложно догадаться, что использование with для единичного обращения к записи бессмысленно:

with L do
  X:=5;

В этом случае мы ни в чём не выигрываем - только пишем больше кода.

Помните про оператор with и почаще его используйте - и себе жизнь облегчите, и программы станут профессиональнее.

Хранение записей в файлах

Ну вот мы и подошли к тому, для чего пришлось затронуть тему работы с файлами. Использовать записи мы научились, но тут вопрос: а как их хранить? Сохранять в файлах отдельно каждое поле - совершенно неудобно. А потом его нужно оттуда ещё как-то прочитать... Неужели нет способа проще? Есть!

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

Вернёмся к программе, которая позволяет ввести отрезок. Теперь перед нами задача сохранить этот отрезок в файл. Ну, не в буквальном смысле, конечно - просто сохранить все его параметры.

Начнём с создания указателя на файл, который опишем указанным образом:

F: file of TLineSegment;

Этим мы сказали, что каждый элемент нашего файла - запись типа TLineSegment. А дальше всё как обычно - ничего нового: связываем указатель с файлом, открываем, записываем, закрываем. Без комментариев, что называется:

AssignFile(F,ExtractFilePath(Application.ExeName)+'lines.dat');
Rewrite(F);
Write(F,L);
CloseFile(F);

Всё это нужно добавить в конец обработчика кнопки "Сохранить". Запустите программу, введите произвольный отрезок и нажмите кнопку. Если всё было сделано правильно, после сообщения о длине отрезка в папке с программой появится файл lines.dat. Расширение dat - стандартное для нестандартных данных (вот так фразу завернул!). Вы можете открыть этот файл любым текстовым редактором, но прочитать что-либо там будет затруднительно - это бинарный файл.

Теперь попробуем прочитать данные из этого файла. Создайте ещё одну кнопку "Загрузить". Код для неё будет такой:

procedure TForm1.LoadButtonClick(Sender: TObject);
var F: file of TLineSegment; L: TLineSegment;
begin
  AssignFile(F,ExtractFilePath(Application.ExeName)+'lines.dat');
  Reset(F);
  Read(F,L);
  CloseFile(F);
  MessageDlg('Длина отрезка: '+FloatToStr(GetLength(L)),mtInformation,[mbOk],0);
end;

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

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

Чтобы провернуть всё это, пришлось добавить 2 переменные - ещё одну запись и один файл. Под буквами "N" и "O" я подразумеваю "new" и "old" (новый и старый).

procedure TForm1.SaveButtonClick(Sender: TObject);
var
  L,OL: TLineSegment;
  F,NF: file of TLineSegment;
begin
  {предыдущий код здесь опущен}
 
  SetCurrentDir(ExtractFilePath(Application.ExeName)+'lines.dat');
  AssignFile(NF,'temp.dat');
  Rewrite(NF);
  if FileExists('lines.dat') then
  begin
    AssignFile(F,'lines.dat');
    Reset(F);
    while not EOF(F) do
    begin
      Read(F,OL);
      Write(NF,OL);
    end;
    CloseFile(F);
  end;
  Write(NF,L);
  CloseFile(NF);
  DeleteFile('lines.dat');
  RenameFile('temp.dat','lines.dat');
end;

Разберём этот код подробно. Сначала делаем папку с программой рабочей, чтобы каждый раз не писать путь. Далее ассоциируем указатель с новым файлом, и открываем этот файл для записи. Далее проверяем, есть ли файл с предыдущими записями. Как мы договорились, если он есть, то нужно переписать из него все записи: связываемся с файлом, открываем его для чтения, а далее цикл по всем полям записи. Функция EOF() позволяет узнать, дошли ли мы до конца файла. Таким образом, пока файл не кончился, читаем из него одну запись и переписываем её в новый файл. После завершения закрываем старый файл. Осталось самое простое - записать новую запись, что мы и делаем. После этого старый файл lines.dat удаляем, а временный temp.dat переименовываем в новый lines.dat. Таким образом, достигнута требуемая цель. Запустите программу и добавьте в наш файл ещё несколько отрезков. О том, что добавление происходит успешно, можно судить по увеличивающемуся объёму файла.

Следующая задача: узнать, сколько записей в файле. Делается это очень просто - функцией FileSize(). Когда мы объявляем "файл из байтов" (file of byte), то получаем объём файла в байтах. Сейчас же мы узнаем, сколько записей в файле:

var F: file of TLineSegment;
{...}
AssignFile(F,ExtractFilePath(Application.ExeName)+'lines.dat');
Reset(F);
ShowMessage('В файле '+IntToStr(FileSize(F))+' записей');
CloseFile(F);

Код очень простой и понятный.

Ну и наконец последнее, о чём мне хотелось бы рассказать - это о "перемотке" файла. Я имею ввиду о том, как добраться до записи в середине файла, не перебирая все предыдущие через Read(). Такая задача встречается очень часто и решается она достаточно просто. Процедура Seek() перемещается по файлу на указанный по номеру элемент:

Seek(указатель_на_файл,номер_записи);

Пример: добавим на форму TListBox и кнопку "Обновить список":

procedure TForm1.UpdateButtonClick(Sender: TObject);
var F: file of TLineSegment; I,N: Integer;
begin
  AssignFile(F,ExtractFilePath(Application.ExeName)+'lines.dat');
  Reset(F);
  N:=FileSize(F);
  CloseFile(F);
  ListBox1.Items.Clear;
  for I := 1 to N do
    ListBox1.Items.Add('Запись #'+IntToStr(I))
end;
Тестовая программа

Как видно, эта кнопка добавляет в ListBox список записей в файле. Теперь кнопка "Загрузить" должна загрузить выбранную в списке запись и отобразить длину отрезка:

procedure TForm1.LoadButtonClick(Sender: TObject);
var F: file of TLineSegment; L: TLineSegment;
begin
  AssignFile(F,ExtractFilePath(Application.ExeName)+'lines.dat');
  Reset(F);
  Seek(F,ListBox1.ItemIndex);
  Read(F,L);
  CloseFile(F);
  MessageDlg('Длина отрезка: '+FloatToStr(GetLength(L)),mtInformation,[mbOk],0);
end;

Свойство ItemIndex у ListBox определяет номер строки, выбранной в данный момент (строки нумеруются с нуля). После открытия файла мы прыгаем на запись с таким номером, читаем её и затем определяем длину.

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

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

Домашнее задание

Для закрепления пройденного не буду предлагать новой программы - давайте изменим эту. Итак, требуется:

  1. Создать функцию, которая определяет угол наклона заданного отрезка. Не забывайте о модуле Math - там есть вещи, которые вам пригодятся.
  2. В списке записей в ListBox в каждой строке нужно указать координату одного из концов отрезка. Например, вместо "Запись #2" должно выводиться "Запись #2 (1;2)".
  3. Кнопка "Сохранить" должна добавлять новую запись не в конец файла, а после той, что выбрана в ListBox. Например, если выбрана №2, то новая будет сохранена на 3-ю позицию, а все остальные таким образом сдвинутся вниз.

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

Желаю успехов! До встречи на следующем уроке!


Оценить данный урок, а также оставить свои комментарии Вы можете, перейдя на сайт: страница этого урока на сайте »
 

Статьи

Леворекурсивный парсер

Автор: Вадим К

Введение

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

Для такого преобразования обычно применяют алгоритмы, которые называются парсерами. Для определённого круга задач уже давно написаны свои готовые парсеры. Например для анализа XML. В случае простых данных можно обычно обойтись простыми функциями Pos/Copy. Но как только данные чуточку усложняются – код становится огромным и неудобным. И каждое новое добавление функциональности превращается в пытку и бессонные ночи отладки.

Простое решение

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

В нескольких статьях я попробую рассказать, как написать один из простейших вариантов парсера – леворекурсивный. При правильной реализации этот парсер является одним из самых быстрых. Но он не может распарсить абсолютно всё. Например, код на языке Pascal можно распарсить с помощью чистого леворекурсивного парсера. Начиная с первых версий Delphi, парсер не такой уж и чисто леворекурсивный, однако он и не слишком усложнён. Код на языке С++ нельзя распарсить этим парсером. Для этого языка применяется парсер с возвратами. Это одна из причин, почему компилятор Делфи значительно быстрее компилятора С++. Хотя есть и ещё десяток причин :-)

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

Как это работает?

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

Примитивный парсер или изобретаем свой StrToInt

Скорее всего, вам известна функция StrToInt. Она позволяет из строки, которая содержит целое число, получить собственно само число, с которым может работать процессор. Строки, содержащие числа, мы ведь не можем уммножать...

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

Для начала сделаем примитивную форму для тестов, которую мы будем использовать в большинстве последующих примеров. Для кнопки «Расчёт!» напишем такой код:

procedure TForm1.Button1Click(Sender:TObject);
begin
 Edit2.Text := FloatToStr(Parser(Edit1.Text));
end;

Форма

Функция Parser – это наша функция, которая получает строку и отдаёт число. Правда мы тут же его преобразовываем снова в строку. Но это пока. И пусть не смущает то, что я говорил о IntToStr, а использую дробные числа. Всё станет на свои места позже.

Заготовка парсера

Привожу код самого парсера с комментариями. (Полный проект – в папке Demo1).

unit MyParser;
 
interface
 uses SysUtils;
function Parser(s: string): double;
 
implementation
 
var
 InpStr: string; //Копия входной строки
InpPos: integer;//Номер текущего символа
 CurrChar: char; //Копия текущего символа
 
//Процедура берёт следующий символ из строки
procedure GetNextChar;
begin
 if InpPos < length(InpStr) then begin
 Inc(InpPos);
 CurrChar := InpStr[InpPos];
 end
 else
 CurrChar := #0;
end;
 
//Функция чтения числа
function GetNumber:Double;
begin
 result := 0;
 while CurrChar in ['0'..'9'] do begin
 result := result * 10 + ord(CurrChar) -
ord('0');
 GetNextChar;
 end;
end;
 
//Парсер :)
function Parse: double;
begin
 result := GetNumber;
 if CurrChar <> #0 then
 raise Exception.create('В конце строки неизвестные символы!');
end;
 
//Иницализация и запуск парсера
function Parser(s: string): double;
begin
 InpStr := s;
 InpPos := 0;
 GetNextChar;
 Result := Parse;
end;
 
end.

Этот код будет основой для всех последующих парсеров. Давайте кратко разберём фунции.

Функция Parser. Эта функция вначале инициализирует внутренние переменные (первые две строки), читает первый символ (он автоматически помещается в глобальную переменную CurrChar) и последней строкой вызывает функцию, которая, собственно, и делает парсинг.

Функция Parse также не делает ничего сложного. Она читает число из потока (об этом ниже). После того, как она прочитала число, там больше ничего не должно быть, это ведь функция преобразования строки в число. Если там что-то обнаружено – генерируем исключение.

Продвигаемся дальше вглубь. Функция GetNumber. Перед её анализом, давайте подумаем, что такое обычное целое число. Это просто последовательность цифр. А теперь смотрим на функцию. Она работает просто. Проверяет текущий символ: если он - цифра, то сохранённый результат умножает на 10 и добавляет значение цифры. Перевод из символа цифры в число я делаю конструкцией Ord(CurrChar) - Ord('0'). Это очень старый, но очень быстрый способ. Можно было, конечно, использовать функцию StrToInt, но смысл? :-) После обработки текущего символа переходим к новому (процедура GetNextChar).

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

Заметье, что функция GetNumber читает до тех пор, пока в входном потоке есть цифры. Если их там нет – она выходит. Её абсолютно не волнует, что там дальше. Это забота другого кода.

И на последок рассмотрим процедуру GetNextChar. Она работает тоже крайне примитивно. Пока в строке есть ещё символы – она увеличивает счётчик и присваевает CurrChar текущий символ. Если символов больше нет – возвращает нулевой символ.

А теперь попробуйте ответить на простой вопрос. Знает ли функция Parse или GetNumber о том, откуда берутся следующие символы? Нет! Им этого и не нужно знать. Только процедура GetNextChar знает, как их получить, ну и функция Parser умеет подготовить данные. Это позволяет с лёгкостью заметить источник данных без переделки всего кода. Например, захотели мы читать данные из файла. Нам надо переделать только эти две функции. А как именно – это будет первым домашним заданием.

Первое улучшение

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

Первое – напишем простую процедуру:

procedure SkipSpace;
begin
 while CurrChar in [' ', #9] do
 GetNextChar;
end;

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

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

//Функция чтения числа
function GetNumber:Double;
begin
 result := 0;
 SkipSpace;
 while CurrChar in ['0'..'9'] do begin
 result := result * 10 + ord(CurrChar) - ord('0');
 GetNextChar;
 end;
 SkipSpace;
end;

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

Простой калькулятор

Я надеюсь, вы заметили название формы – простой калькулятор. И мы сейчас его сделаем! Правда, он будет уметь только складывать и вычитать числа. Но зато он будет уметь это делать!

Итак, научим наш калькулятор понимать выражения вида «1 + 23 + 456 - 789» Заметьте, с пробелами, и со знаками плюс и минус. Для начала посмотрим на эту строку. Что же мы видим? Вначале надо прочитать число, потом в цикле читать знак и ещё одно число, и производить операцию.

Посмотрите на то, что написано ниже:

//Парсер :)
function Parse: double;
begin
 Result := GetNumber;
 repeat
 case CurrChar of
 #0: exit; //Достигли конца строки
 '+': //Нужно сложить
 begin
 GetNextChar;
 Result := Result + GetNumber;
 end;
 '-': //Нужно вычесть
 begin
 GetNextChar;
 Result := Result - GetNumber;
 end;
 else //Какой-то неизвестный символ.
 raise Exception.CreateFmt(
 'Я пока умею складывать и вычитать!'#13#10+
 ' В строке обнаружен символ %s в позиции %d',
          [CurrChar, InpPos]);
 end;
 until False;
end;

Как видно – ничего больше, чем я сказал, когда давал определение.

Домашнее задание

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

Научите парсер уммножать и делить. Пока что без учёта старшинства операций. Можно и степень ввести (символ ^).

Научите парсер понимать выражение вида PI * 2, где PI – это 3.14, то есть, число пи. Хотя это и кажется сложным заданием, на самом деле оно очень простое.

Найдите и попробуйте исправить как минимум две ошибки в реализации функции GetNumber.

А дальше?

В следующей части мы научим наш парсер работать с дробными числами и с шестнадцатеричными.

Примеры к статье (исходники 3 демо-проектов) »


Оценить данную статью, а также оставить свои комментарии Вы можете на нашем сайте. Перейти на страницу со статьёй »
 

Файловый архив

Разделы: Статьи | Компоненты | Plug-in's | Документация | Исходники | Программы | Игры | Изображения

Уважаемые читатели! Отправляйте полезные компоненты/модули, исходники, собственные программы/игры, документацию - книги, учебники и они будут размещены на сайте.

Название
Описание
Раздел
Объём
Ссылки
NewSpeedButton
TNewSpeedButton - Кнопка, подобная той, что используется в программе QIP Infium.
182 Кб
Dp7Lib v3.0
Что нового? Компоненты TdpTrayIcon и TdpMeleeTrayIcon подверглись модернизации, но не большой (исправлены мелкие ошибки). TdpChoiceSpectr переименован в TdpSpectr (так в принципе и должно было быть) и тоже был подвержен модернизации (много лишнего в нём было). Исправлена ошибка в названии компонента TdpColourSpectr (недоставало буквы "u"). Компонент TdpGradSpectr удален из за лёгкости его создания (куча ненужного кода). TdpPipetteShape теперь потомок TShape. Добавлены компоненты TdpButton (кнопка), TdpBalloon (воздушный шар), TdpBalloonButton (кнопка закрыть для TdpBalloon), TdpDirectoryChange (позволяет ловить директивные изменения, пришёл на смену класса TdpFileChange), TdpNotice (всплывающее сообщение - можно формировать в дизайне), TdpTrayNotice (своё сообщение в Notification area). Совместимость: Delphi 4, 5, 6, 7, 9, 10, 11, 12.
176 Кб
Easy Text

Easy Text - текстовый редактор с открытым исходным кодом.

Данный текстовый редактор построен специально для начинающих программистов, в нем вложены все стандартные функции текстового редактора, такие как создание, открытие, вставка из другого файла в текущую позицию курсора, сохранение, печать документа, поддержка механизма Drag & Drop, сохранение шрифта при выходе из программы, перенос по словам, поиск, поиск далее и замена текста, вставка даты и времени, переход на строку, подтверждение на замену файла, если таковой уже существует, отображение в строке статуса текущей строки и количество символов в ней, изменение отображения открытого файла в строке заголовка окна и на панели задач (ReadMe.txt - Easy Texter, Easy Texter - ReadMe.txt, C:\ReadMe.txt - Easy Texter, Easy Texter - C:\ReadMe.txt) и некоторые другие. Некоторые настройки сохраняются в INI-файлах (положения и размер окна и др.), набранный текст всегда можно отправить по электронной почте программой установленной в вашей системе по умолчанию.

352 Кб


One Channel IRC Client
Очень простой IRC-клиент, рассчитанный на работу с одним каналом. В архиве находятся исходники клиента и модуль для работы с IRC. Клиент настроен на IRC-канал сайта и готов к использованию.

26 Кб

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

8 Кб

Электронное тестирование ЕГЭ
Программа предназначена для моделирования условий итоговой аттестации выпускников 11-х классов по предмету «Информатика и ИКТ» в рамках Единого государственного экзамена с целью адаптации учащихся к постановке заданий будущего тестирования.

844 Кб

Inscription On Screen
С помощью программы "InscriptionOnScreen" вы сможете создавать надписи или рисунки на экране вашего монитора. Для этого просто запустите программу и начинайте рисовать. После этого нажмите кнопку "Старт" для того, чтобы увидеть, что получилось. Так же имеется возможность сохранить проект в *.ios файл.

682 Кб

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

615 Кб

OC Irc
Простой IRC-клиент, с помощью которого можно находиться в IRC на одном канале. Минимальный набор функций, необходимых для работы в IRC. Клиент изначально настроен на канал сайта www.delphi.int.ru.

260 Кб

Text Art Utils
Програма для текстово-графических манипуляций:
- превращает картинку в цветной текст (с возможностью сохранения в HTML);
- рисует заданный текст текстом;
- позволяет рисовать текстом.
Исходники программы в комплекте.

1.34 Мб

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

580 Кб

Карточная игра "50"
РГЗ по дисциплине "Системы искусственного интеллекта".
Тема: программирование игр и головоломок.
Суть игры: На столе располагаются 24 раскрытые карты: все карты с номерами от 1 до 6 обычной колоды, где туз считается за 1. Масти карт несущественны. Каждый игрок при своём ходе берёт со стола карту и складывает её значение с суммой тех, которые были взяты ранее (таким образом, подсчитывается общая сумма карт, взятых партнёрами, а не отдельные суммы для каждого партнёра). Первый, кто набирает в точности 50 очков, выигрывает. Если игрок, взяв карту, не может не превысить 50 очков, то он проигрывает.
Помимо самой игры в архиве находятся её исходники.

960 Кб

 
Представлено файлов: 12  
6.03 Мб
 
 

Юмор

Ведущий раздела: Bruder

50 причин, почему компьютеры лучше человека

1. Компьютер предназначен для того, чтобы исполнять ваши желания.

2. У компьютера есть регулятор громкости и кнопка выключения звука.

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

4. Чтобы компьютер стал вашим, достаточно заплатить деньги один раз, причем никто вас за это не осудит.

5. Вы можете заказать и получить именно ту конфигурацию компьютера, которая вам нужна.

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

7. Компьютер согласен жить на четверти квадратного метра, да и та на столе.

8. Питание компьютера не требует полуторачасовой возни на кухне.

9. Компьютер не требует от вас разнообразить его меню.

10. Компьютер не чавкает.

11. Вам не придется назначать компьютеру встречу в метро, на которую он к тому же опоздает.

12. На компьютеры дают гарантию.

13. Компьютер не суеверен.

14. Компьютер не курит.

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

16. Компьютер не смотрит сериалы.

17. Компьютер не смотрит футбольные матчи.

18. Компьютер занимает телефон только по делу. Причем по вашему.

19. Родственники компьютера, как правило, не вызывают у вас раздражения.

20. Вам не нужно дарить компьютеру подарки на день рожденья.

21. Вам вообще не нужно помнить, когда у компьютера день рожденья.

22. Компьютер не празднует 8 марта.

23. Компьютер не празднует 23 февраля.

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

25. У компьютера не бывает носков.

26. Компьютер не пользуется косметикой.

27. У компьютера не растет щетина.

28. Компьютер не потеет.

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

30. Компьютер ни при каких обстоятельствах не возьмет вашу зубную щетку.

31. Ночь, проведенная с компьютером, едва ли обернется для вас неприятностями в будущем.

32. У компьютера нет политических взглядов.

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

34. Чтобы прочистить компьютеру мозги, достаточно одного нажатия на кнопку.

35. Компьютер запоминает с первого раза.

36. Но если надо, чтобы компьютер что-то забыл - это делается одной командой.

37. Компьютер не комплексует по поводу размера и формы своих комплектующих.

38. Компьютер не волнует, где вас носило до двух часов ночи.

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

40. Компьютер не храпит.

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

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

43. Компьютерные вирусы не передаются воздушно-капельным путем.

44. Существуют простые тесты, способные рассказать вам все о вашем компьютере.

45. Менять настройки компьютера легко и просто.

46. Причем, как правило, изменение одной не приводит к непредсказуемому изменению остальных.

47. Компьютер не надоедает вам просьбами научить его работать с компьютером.

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

49. Компьютер можно заменять по частям.

50. Ни закон, ни мораль не запрещают вырубить компьютер.

Источник: bayanov.net

:))

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




Ведущий рассылки: Ерёмин Андрей


В избранное