Урок 3. Генератор писем
— А кто вам говорит, что вы должны сочинять какие-то фразы?
Отвлечемся на время от программирования и вспомним мировую классику.
Стендаль. «Красное и черное». Главный герой Жюльен общается со своим другом, русским князем Коразовым. Князь интересуется у Жюльена причинами его подавленного состояния. Жюльен рассказывает, что влюбился в госпожу де Дюбуа. Что та три дня страстно любила его, а затем прогнала. И что теперь он убит этим. Что предлагает князь? Простую схему: влюбить в себя какую-нибудь светскую красавицу и сделать так, чтобы госпожа де Дюбуа узнала об этом. То есть сыграть на чувстве ревности. При этом Жюльен должен соблюдать несколько простых правил: относиться к госпоже де Дюбуа так, как будто никакого разрыва не было; к светской красавице, которую он возьмется окручивать, не проявлять никаких чувств на публике, а всю свою страсть к ней выражать в письмах. Таким образом, госпожа де Дюбуа увидела бы востребованность Жюльена у противоположного пола, но при этом оставалась бы абсолютно уверенной в его чувствах к ней (именно поэтому Жюльен должен был разжигать страсть выбранной им светской красавицы через письма).
Так вот, катайте ей по два письма в день. — Ни за что, ни за что! — испуганно воскликнул Жюльен. — Пусть меня лучше живьем истолкут в ступе! Я не способен сочинить и двух фраз, я совершенный труп, дорогой мой, ничего от меня ждать нельзя. Бросьте меня, вот я здесь лягу и умру на краю дороги.
— А кто вам говорит, что вы должны сочинять какие-то фразы? У меня с собой в дорожной сумке лежит шесть томов любовных писем. Всех сортов, на любой женский характер. Найдутся и для образцовой добродетели! Ведь вот же Калисский волочился в Ричмонд-Террасе — это в трех лье от Лондона — за самой хорошенькой квакершей во всей Англии.
Когда в два часа ночи Жюльен расстался со своим другом, он чувствовал себя уже не столь несчастным.
На другой день князь пригласил на дом переписчика, а через два дня Жюльен получил пятьдесят три любовных письма, тщательно перенумерованных и предназначенных для одоления самой возвышенной и самой унылой добродетели.
Чем всё закончилось? Схема князя сработала. Но были некоторые сбои: Жюльен переписывал письма не очень внимательно и местами забывал заменить имя возлюбленной Калисского (автора писем) на имя светской красавицы, которую Жюльен хотел влюбить в себя.
А если бы у него был компьютер и письма были в электронном виде? Можно ли было избежать подобных ошибок и каким образом?
Давайте промоделируем эту ситуацию.
Конечно, мы будем моделировать её очень условно. Текст нашего письма будет выводиться на экран, а не в файл, который можно было бы переслать по электронной почте. Но сейчас это неважно, т. к. для нас главное — понять ряд принципов.
Итак, смотрим:
program letter; uses Crt; begin ClrScr; Writeln( 'Здравствуй, Милен!' ); Writeln( '...' ); Writeln( 'Засыпаю с твоим именем на устах — Милен, моя Милен' ); end.
Надеюсь, вы не ждали настоящего любовного письма :) Всё-таки мы занимаемся программированием, а не учим азы любовной переписки.
Сохраните программу (F2 — File | Save) под именем letter.pas, а затем запустите её (Ctrl + F9) и посмотрите результаты работы программы в соответствующем окне (Alt + F5).
Что мы видим в этой программе? Программа очищает экран (с помощью процедуры ClrScr) и выводит на экран текст письма. В письме 3 раза встречается имя «Милен».
Допустим, нам нужно сформировать новое письмо, заменив имя «Милен» на «Элен». Как это сделать? С такой программой как выше у нас есть только один способ: пройтись по всему письму и заменить везде вручную имя «Милен» на «Элен». При этом мы рискуем допустить такую же ошибку как Жюльен, проморгав где-нибудь имя «Милен» и оставив его без изменений.
На самом деле у нас есть еще один способ: воспользоваться такой возможностью редактора Free Pascal как «Заменить...» (через меню Search | Replace...). Но об этой возможности я расскажу в конце урока.
А сейчас мы изменим нашу программу так, чтобы замену имени в письме можно было производить «в одно касание», не прибегая к возможностям редактора Free Pascal, и уж тем более, не заменяя имя вручную по всему письму.
Смотрим новый код:
program letterName; uses Crt; const Name = 'Милен'; begin ClrScr; Write( 'Здравствуй, ' ); Write( Name ); Writeln( '!' ); Writeln( '...' ); Write( 'Засыпаю с твоим именем на устах — ' ); Write( Name ); Write( ', моя ' ); Writeln( Name ); end.
Сохраните новую программу под именем letterName.pas и запустите её (Ctrl + F9). Нажмите Alt + F5, чтобы посмотреть результат её работы.
Нажмите любую клавишу, чтобы вернуться в редактор. Найдите строку «const Name = 'Милен'» и замените имя «Милен» на «Элен».
Запустите программу снова (Ctrl + F9) и убедитесь, что на экран выводится теперь новое письмо (Alt + F5):
Проанализируем новый код. У нас появилась новая строка между «uses Crt;» и «begin»:
const Name = 'Милен';
«const» означает «константа». Константа — это постоянная величина, то, что не меняется. В этой строке мы «говорим» компилятору, что в нашей программе мы будем использовать константу с названием «Name», и здесь же указываем её значение: «'Милен'».
Здесь у вас может возникнуть вопрос: почему же Name — константа, если мы потом меняем её значение с 'Милен' на 'Элен'? Да, мы меняем значение Name, но мы делаем это до запуска программы и меняем его мы (!), а не сама программа. Когда мы объявляем Name как константу, то мы тем самым запрещаем изменять её значение программе (!). Конечно, можно продолжить и дальше задавать вопросы: например, как программа может сама что-то менять, если программу пишем мы, а значит, заранее знаем, что она будет делать, и если мы не хотим, чтобы какие-то значения менялись, то просто должны не менять их, т. е. не писать соответствующий код в своей программе? Это верный вопрос, но он выходит за рамки нашего текущего урока, и я останавлюсь на нём позже, когда мы начнем проходить переменные.
После того, как мы объявили константу Name, мы можем использовать её в своей программе везде вместо строки 'Милен'.
Вернемся на секунду к нашей первой программе в этом уроке (файлу letter.pas), а именно к строчке:
Writeln( 'Здравствуй, Милен!' );
Можем ли мы заменить эту строчку на такую?
Writeln( 'Здравствуй, Name!' );
Заменить-то мы можем, только результат на экране будет совсем не такой, как нам нужен:
Здравствуй, Name!
Запомните: всё, что находится между одинарными кавычками выводится на экран как обычный текст. Для компилятора 'Name' — это текст!
Если же мы напишем:
Writeln( Name );
то получим на экране:
Милен
Видите разницу между этими двумя командами: Writeln( 'Name' ) и Writeln( Name )?
В первом случае мы получаем на экране текст «Name».
Во втором — компилятор ищет идентификатор Name, и в нашем случае находит его в разделе констант. Далее компилятор берет значение ('Милен'), которое мы указали для константы Name, и даёт его процедуре Writeln. В результате процедура Writeln выводит на экран имя «Милен».
Чтобы вы наглядно представили себе эту разницу, я приведу такую аналогию. Представьте себе набор ящиков для хранения (например, как в супермаркете, куда вы складываете свои вещи, прежде чем зайти в торговый зал). И представьте себе, что у вас есть полоска бумаги, на которой написано Name. Так вот, полоска бумаги с надписью Name в нашем примере — это строка 'Name'. А тогда, что такое константа Name в нашей аналогии? А это ящик, на котором написано Name, и в котором лежит полоска бумаги с надписью 'Милен', выведенной ручкой, а не карандашом (то есть изменить надпись после запуска программы уже нельзя).
Когда мы пишем Writeln( 'Name' ), мы даем процедуре Writeln полоску бумаги с надписью Name. А когда мы пишем Writeln( Name ), то мы как бы говорим компилятору, что нужно залезть в ящик, на котором написано Name, взять полоску бумаги оттуда и уже её дать процедуре Writeln для вывода на экран.
Снова возвращаемся к строке:
Writeln( 'Здравствуй, Милен!' );
После того, как мы ввели константу Name, мы переписали эту строку в программе letterName в следующем виде:
Write( 'Здравствуй, ' ); Write( Name ); Writeln( '!' );
В чем разница между Writeln и Write? После вывода текста на экран процедура Writeln переводит курсор на следующую строку (помните, ту строку, которая line по-английски?). «ln» в конце «Writeln» — это сокращение от слова line. Процедура же Write выводит текст на экран, оставляя курсор в той же строке, за последним напечатанным символом.
Смотрите как отличается вывод двух команд:
Write( 'Здравствуй, ' );
Здравствуй, _
Writeln( 'Здравствуй, ' );
Здравствуй,
_
Знаком «_» я показываю положение курсора.
Остальные строки программы вы можете проанализировать сами.
О процедурах вообще и Write/Writeln в частности
Мы с вами еще на первом уроке познакомились с процедурой Writeln. Что такое процедура? В самом начале, чтобы вам было легче усвоить понятие «процедура», я использовал его параллельно с синонимом «команда». В реальности, конечно, всё немного сложнее. Как правило, в обычной жизни мы понимаем под командами какие-то простые, понятные действия. Наиболее уместной тут будет аналогия с армией. Какие бывают команды? — Равняйсь, смирно, вольно, шагом марш, налево и т. д. В то же время, если мы возьмем какой-нибудь ракетный полк и рассмотрим команду «поразить цель такую-то», то за этой командой уже будет идти целая последовательность как простых, так и сложных действий. Например, выйти на заданную позицию, перевести ракеты в боевое положение, задать координаты цели и т. д. и т. п. То есть команду «поразить заданную цель» уже можно рассматривать как некую процедуру.
Еще обратите внимание, что у процедуры могут быть входные параметры. Что будет входными параметрами для процедуры поражения цели? Например, координаты цели и количество ракет, которые должны одновременно ударить по цели.
Возвращаясь к программированию, процедура может и не иметь входных параметров. Например, процедура ClrScr, которая очищает экран, не требует никаких данных. Или процедура Writeln, которая допускает отсутствие входных параметров. Если процедуре Writeln вы не передадите какой-либо текст, то тогда всё, что она сделает — это переведет курсор на следующую строку экрана (об этом упоминалось в разделе «Задания повышенной сложности» урока 2).
В этом плане процедуры Write/Writeln несколько отличаются от типовых процедур. Как правило, процедуры имеют фиксированное число параметров. Write/Writeln же могут принимать произвольное число параметров.
Вернемся к нашей программе letterName, а именно к строкам:
Write( 'Здравствуй, ' ); Write( Name ); Writeln( '!' );
Благодаря тому, что Write/Writeln могут принимать произвольное число входных параметров, приведенные строки можно сократить до одной строки:
Writeln( 'Здравствуй, ', Name, '!' );
Чтобы вы не запутались в запятых, я покажу как правильно набирать эту строку по шагам:
Writeln(); Writeln( '', Name, '' ); Writeln( 'Здравствуй, ', Name, '!' );
Посмотрите внимательно на вторую строку: мы передаем процедуре Writeln три входных параметра: строку, константу (являющуюся тоже строкой), и снова строку. Все параметры перечисляются через запятую. То есть: параметр, запятая, параметр, запятая, параметр (дальше запятая уже не нужна, т. к. идет закрывающая скобка).
Перепишем программу letterName с учетом того, что мы теперь знаем о процедуре Writeln.
О возможностях редактора Free Pascal — Search | Replace...
Выше я обещал вам рассказать, как можно быстро произвести замену какого-либо слова в своей программе на любое другое с помощью редактора Free Pascal.
Вернемся к исходной программе letter.
program letter; uses Crt; begin ClrScr; Writeln( 'Здравствуй, Милен!' ); Writeln( '...' ); Writeln( 'Засыпаю с твоим именем на устах — Милен, моя Милен' ); end.
Чтобы заменить «Милен» на «Элен» с помощью редактора, нажмите Ctrl-Q A (или войдите в меню Search | Replace...).
Здесь нужно пояснить как нажать Ctrl-Q A: нажмите сначала Ctrl + Q, а потом (отпустив клавиши Ctrl и Q) нажмите клавишу А.
Появится окно Replace, в котором вы должны заполнить следующие поля:
Text to find (текст, который требуется найти) = Милен
New text (новый текст, тот, на который вы хотите заменить) = Элен
Убедитесь, что Scope (область поиска) = Global (по всему тексту), а Origin (с какого места искать) = Entire scope (по всей области поиска).
И на всякий случай установите галочку Prompt on replace (запрашивать подтверждение для каждой замены).
Нажмите «Change all» (заменить все найденные слова).
Каждый раз находя искомое слово (которое мы указали в поле Text to find), редактор будет спрашивать подтверждения на замену (помните, мы устанавливали галочку Prompt on replace?):
Нажимайте «Yes» («Да»), если вас устраивает предлагаемая замена.
Иногда бывает важно, чтобы редактор отличал регистр букв в слове, то есть, чтобы слова «Милен» и «милен» считались разными. Для этого при замене нужно устанавливать галочку Case sensitive (чувствителен к регистру) — см. окно «Replace» на предыдущем снимке экрана.
И чтобы больше не возвращаться к этому окну:
- Whole words only означает, что заменять нужно только целые слова (представьте, что у вас в тексте есть слово «Миленькая», при замене «Милен» на «Элен» без установленной галочки Whole words only, ваша «Миленькая» превратится в «Эленькая»).
- Scope = Selected text означает, что замену нужно производить только в выделенном фрагменте текста, а не во всей программе.
Немного о генерации писем
При покупке авиабилетов, бронировании гостиниц, или во время обычных покупок в интернет-магазинах, вас часто просят зарегистрироваться на сайте: указать свои фамилию, имя, адрес электронной почты. Впоследствии это позволяет авиакомпаниям, отелям, магазинам (и т. д.) присылать вам адресные письма. То есть открывая это письмо, вы увидите в нем не безличное «Уважаемый клиент» или «покупатель», а, например, «Дмитрий, ещё пара дней, и скидки на перелёты по промокоду time2fly закончатся...». Такие письма (адресные) больше располагают к себе, чем безличные.
Прочитав этот урок, вы уже понимаете, что генерацией этих писем занимаются специальные программы. Есть шаблоны писем и база данных, в которой хранятся данные о клиентах авиакомпании/магазина/и т. д. Программа извлекает из базы данных фамилию, имя клиента и подставляет их в нужное место в шаблоне письма. Конечно, подобные программы гораздо сложнее, чем тот пример, что мы разобрали выше, но принцип остается тот же: строка текста хранится в программе не в готовом виде, а формируется «на лету» на основе входных данных. Да, в нашей программе входные данные (имя возлюбленной) прописывались жестко в самой программе в виде константы, а не «приходили» извне (из базы данных, например), но сути это не меняет.
Впоследствии мы еще вернемся к примеру, разобранному в этом уроке, и модифицируем (изменим) нашу программу так, чтобы имя возлюбленной запрашивалось у пользователя после запуска программы. Или вы сами модифицируете программу (в рамках задания), после того как освоите необходимые инструменты.
Краткое содержание урока
✔ Мы узнали как заменять текст в программе с помощью редактора Free Pascal:
- Ctrl-Q A (Search | Replace...)
✔ Узнали новое ключевое слово Паскаля:
- const — ключевое слово, после которого объявляются все константы, используемые в программе.
✔ Познакомились с еще одной процедурой:
- Write — выводит на экран текст, оставляя курсор в той же строке (в отличие от Writeln, который переводит курсор на новую строку).
✔ Выяснили, что процедуры Write/Writeln могут принимать произвольное число входных параметров.
✔ Узнали, что такое генерация текста и зачем она может быть нужна.
Задания
- Напишите программу, которая выводит на экран текст для клиента авиакомпании S7:
Дмитрий,
Ещё пара дней, и скидки на перелёты по промокоду time2fly закончатся. Успейте воспользоваться! Если вы ничего не успели спланировать — не беда, мы подобрали отличные первомайские маршруты.
Указывайте ваш номер S7 Priority 804804804 при покупке билета или регистрации на рейс — теперь за каждый новый полёт рейсами S7 Airlines вы получите 500 или более миль.
Подумайте, что должно быть вынесено в отдельные константы.
Подсказка: даже если у вас в программе две и более констант, слово const используется один раз, например:
const FirstName = 'Иван'; LastName = 'Мельников'; Age = '35 лет';
При этом каждая новая константа идет отдельной строкой и заканчивается обязательно точкой с запятой. Еще обратите внимание, что значения констант выравнены по левому краю (выравнены знаки «=»). Это улучшает читаемость кода. Сравните:
const FirstName = 'Иван'; LastName = 'Мельников'; Age = '35 лет';
- Войдите, чтобы оставлять комментарии