Основы языка Visual FoxPro

Процедуры и функции

Как уже отмечалось выше, язык VFP это сильно дополненный и расширенный язык xBase. В Visual FoxPro язык программирования объектно-ориентированный, то есть базовой конструкцией языка является понятие класса. Исходный же вариант xBase это чистейший структурный язык, с базовым понятием процедур и функций. Таким образом, современный язык программирования Visual FoxPro допускает совмещать как и программирование «по старинке» описанием массы процедур, так и в стиле ООП, создавая сложную иерархию классов.

Разумный же выбор стиля программирования сделает Ваши программы читабельными, легкими для понимания, с другой стороны, позволит Вам быстро создавать мощные приложения. Как уже говорилось, язык Visual FoxPro сильно перегружен языковыми конструкциями, стандартными функциями и операторами. Это сделано из-за соображений совместимости со старыми версиями FoxPro. Дать полное описание всех конструкций языка представляется невозможным из-за огромного количества материала. Поэтому мы отсылаем читателя к Visual FoxPro Online Documentation справочной базе данных по программированию VFP. Размер этой базы данных около 80 Mb, и ее можно установить при установке самого VFP себе на жесткий диск или же оставить на оригинальном CD-диске. Здесь же мы будем стараться приводить описания только тех языковых конструкций, которые потребуются нам для более полного изложения материала. Если в приводимых программах Вы встретите незнакомую функцию или оператор, попробуйте найти его описание в Visual FoxPro Online Documentation, в крайнем случае, напишите мне, и я обязательно подробно про него расскажу.

Итак, здесь рассматриваются:

1. Понятие типов данных и массивов
2. Венгерская нотация
3. Понятие программной единицы (процедуры) и их типы

4. Операторы

5. Организация процедур и функций

6. Циклы и условные операторы

7. Реакция на события

9. Форма элемент Вашего приложения

Понятие типов данных и массивов

Типы данных.

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

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

Logical: Логический тип, возможные значения .T. или .F. Точки обязательны это наследство старого языка xBase

Numeric (float): Числовой с плавающей точкой

Character (string): Строковый — строка символов. Раньше максимальная длина строки была равна 255 символов, теперь 64 Kb.

Date: Дата. Значение определяется как дата с установленным разделителем в установленном формате в {} скобках. Например, по умолчанию, {12/31/99}. Более подробно см. команду определения типа даты SET DATE.

DateTime: Дата и время. Тоже что и дата, но хранит также и время. Более подробно мы рассмотрим этот тип позже.

Object: Тип-объект. Переменная такого типа хранит или ссылку на объект или значение NULL В этой таблице перечислены наиболее часто используемые типы.

Кроме того, в некоторых случаях, могут создаваться переменные с любым типом данных, разрешенном в таблицах, то есть, например, memo, general и др. (См. команды SCATTER и GATHER).

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

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

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

PUBLIC gcMyName, gdCurrentDate gcMyName = Иван Никитин gdCurrentDate = date()

Частные переменные (приватные) существуют в течение выполнения текущего программного модуля и доступны во всех вызываемых им модулях. Эти переменные автоматически удаляются из памяти при окончании выполнения модуля, в котором они были объявлены и созданы. Такой тип области действия получают все необъявленные явно переменные, кроме массивов. По умолчанию, массивы становятся глобальными. Явно объявить этот тип области действия можно командой PRIVATE.

Локальные переменные существуют и доступны только в том модуле, в котором они объявлены. Эти переменные объявляются командой LOCAL и удаляются из памяти автоматически при завершении выполнения программного модуля. Как мы уже говорили, в VFP все переменные динамические.

Как мы убедились, VFP очень бережно работает с памятью, создавая переменные частными по умолчанию и удаляя их из памяти при завершении модуля, в котором они были созданы. Иногда, правда, приходятся самим удалить переменные. Это можно сделать командой RELEASE. Как мы отмечали выше, переменные не могут менять свой тип. Но, используя удаление переменной из памяти, можно создавать такую же переменную, но с другим типом. Я не представляю себе, для чего это нужно реально, но все же вот пример, как это сделать (После символов && — примечания): PUBLIC gMultiTypeVar && Это глобальная переменная
gMultiTypeVar = 1.00 && Ее тип числовой

Release gMultiTypeVar
gMultiTypeVar = «Hello» && Теперь это строка
Release gMultiTypeVar
gMultiTypeVar = {01/01/2000} && А сейчас — дата

Венгерская нотация

В Вашем приложении могут быть сотни и тысячи переменных. Чтобы избежать путаницы и долгих размышлений при отладке программы типа: «A[i,j] что за массив и чего он здесь вообще делает?» была предложена и успешно используется стройная система именования переменных и других объектов программы.

По родине предложившего эту систему программиста она названа Венгерская нотация. Система именования очень проста. Прилагаемо к VFP, имя переменной начинается с 2-х буквенного префикса, 1-я буква которого область действия переменной, 2-я буква ее тип. То есть:

lc — (local character) локальная символьная
ld — (local date) локальная типа дата
gn — (global numeric) глобальная числовая
ga — (global array) глобальный массив и так далее.

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

ldCurrentDate
gaMyCustomers
gnAppVersion

Всячески следует избегать имен переменных типа A[i,j], K1, L2 R0 и т.п. так часто приводимых в старых учебниках по программированию. Единственным исключением, пожалуй, являются переменные циклов I и J. Это «вечные» имена для создания циклов.

Понятие программной единицы (процедуры) и их типы

Как мы уже говорили, Ваше приложение состоит из какого-то определенного набора программных единиц, или модулей (этот термин авторский, «чистого» понятия модуля в VFP нет). В VFP есть несколько типов программных единиц. На наш взгляд все исполняемые программные единицы в VFP можно отнести к двум большим группам:

Программные единицы
Формы

В отличие от старых версий FoxPro, VFP имеет понятие формы определенного описания окна (формы), которое может самостоятельно исполнятся. В старых версиях FoxPro существовало понятие Экрана (screen).Это не одно и то же, что и форма.

Экран в FoxPro просто способ визуального построения окна, который в дальнейшем в любом случае переводился в исполняемую программу, строящую это окно (файл SPR). Форма же по своей сути является описанием класса окна, который при исполнении порождает объект, готовый к работе (см. ООП).

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

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

Программы и процедуры
Процедурные файлы и описание классов
Функции, определенные пользователем (UDF)
Хранимые процедуры и триггеры
Программы запросов
Программы меню

Четкого различия на уровне операторов между всеми этими группами нет. Просто они по разному строятся (например, запросы и меню стоятся автоматически), и соответственно имеют разное назначение. Здесь мы пока не приводим подробное описание каждой группы, лишь обозначим общую идеологию этого построения и типы файлов, в которых сохраняются эти программные единицы. Любая программная единица хранится в файле. Стандартным расширением для такого файла является расширение PRG. При выполнении или при построении проекта этот файл компилируется в p-код и затем выполняется. Компиляция проходит очень быстро и обычно пользователь VFP даже не замечает время, потраченное VFP на компиляцию при выполнении. Соответственно, при выполнении построенного проекта компиляция уже не нужна, так как проект строится на основе уже скомпилированных модулей.

Наборы готовых «кирпичиков» процедур можно объединять в файлы (об этом см. ниже), что дает нам процедурные файлы. Кроме того, есть большой класс програмных элементов, именуемый User defined function (UDF), но об этом ниже.

Хранимые процедуры это такие же наборы процедур, хранимых в базе данных. Об этом см. раздел «Понятие базы данных». Программы запросов и программы меню строятся автоматически и соответственно имеют расширения QPR и MPR. Как правило, Вам не нужно их менять или править. Более подробно об этом мы рассмотрим в разделах применение RQBE и Построение меню.

Операторы

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

Операторы (команды) VFP могут быть как простыми, так и очень сложными и длинными, равно как и команда, выполняемая оператором, может быть простой, а может быть и весьма сложной и емкой. Конечно же, здесь мы не ставим перед собой цель подробно объяснить все команды VFP, для этого у нас попросту нет ни времени, ни сил, ни объема, и, в конце концов, для этого существует Help (справочная информация), но несколько простых оператором мы рассмотрим.

Итак, оператором в VFP считается одна строка программы. Пустые строки игнорируются. Если оператор не умещается на одной строке, или программисту кажется более наглядным разместить один оператор на нескольких строках, то это можно сделать, поставив в конце строки, которую требуется продолжить символ «точка с запятой», например, здесь приведен один оператор:

SELECT ;
Name, Address ;
FROM Customers ;
INTO TABLE Forieners ;
WHERE not Country = Россия

Организация процедур и функций

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

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

Самая верхняя процедура обычно называется главной (стартовой) программой и, собственно, и есть основной единицей Вашего приложения (хотя это и необязательно, как мы покажем ниже). При вызовах процедуры может случиться так, что какая-то процедура (или функция) вызывает саму себя. Это называется рекурсией. С рекурсией в VFP следует обращаться крайне осторожно. VFP поддерживает до 128 рекурсивных вызовов, не более, поэтому мы настоятельно рекомендуем не пользоваться рекурсивными вызовами.

Процедура может принимать при ее вызове параметры от вызывающей программы. Параметры могут объявляться как частные переменные (ключевое слово PARAMETERS) или как локальные переменные (ключевое слово LPARAMETERS). Как именно объявлять параметры дело Вашего вкуса. Число передаваемых параметров может быть меньше, чем число объявленных параметров. В этом случае «лишние» параметры процедуры инициализируются в значение false (.F.). Пример оформления процедуры:

Procedure MyProc
parameter nScope
? «Число pi=»+str(nScope)
return

Соответственно вызов этой процедуры выглядит так:

Do MyProc with 3.14

Программа, дойдя до оператора Do MyProc, произведет поиск файла myproc.prg (или откомпилированного myproc.fxp) и произведет вызов этой процедуры. При вызове переменной-параметру nScope будет передано значение 3.14, и начнет исполняться процедура, пока не будет достигнут конец файла myproc.prg или не будет встречен оператор RETURN. При этом управление будет передано на следующий оператор после Do MyProc.

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

Do MyProc with 3.14 in MyProcFile,

где MyProcFile имя процедурного файла.

Функции очень похожи на процедуры, за исключением двух ключевых моментов:

Функции могут вызываться из выражения, а не только прямым вызовом DO;
Функции могут возвращать результат своей работы;

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

Итак, функция начинается ключевым словом FUNCTION и завершается словом RETURN. После RETURN обычно следует переменная или выражение, которое будет возвращено вызывающей программе. Если это выражение отсутствует или отсутствует само ключевое слово RETURN, то предполагается возврат логического выражения .T. (истина).

Пример функции, которая вычисляет что-то:

FUNCTION MyFUNC
PARAMETER X, Y, Z
LOCAL Result
Result=x+y+z
RETURN Result

Вызов этой функции может быть таким:

MyVar = MyFunc(MyParam1, MyParam2, MyParam3)

или таким:

Something = MyFunc(Param1) + MyFunc(Param2)

или даже так:

=MyFunc()

Как видно, во втором и третьем случае число передаваемых в функцию параметров меньше, чем объявлено в самой функции. Это не ошибка, «лишние» параметры принимаются как переменные логического типа со значением .F. (ложно). Этот момент следует учитывать,

Число переданных параметров в функции всегда можно узнать с помощью функции PARAMETERS(), или проверить параметры функцией EMPTY(), которая вернет .T., если выражение, переданное в нее, является пустым (то есть, .F. или пустая строка или 0 или пустая дата или NULL)

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

Далее мы увидим, что методы классов тоже реализуются именно как функции. Функции, определенные Вами, в VFP называются User Defined Function, то есть функции, определенные пользователем. UDF это очень мощный инструмент структурного программирования.

Циклы и условные операторы

Если Вы знакомы со структурным программированием, то Вам особенно не нужно объяснять, что такое циклы и условные операторы. Но все же вкратце на этом остановимся.

Условный оператор позволяет выполнять тот или иной блок программы в зависимости от какого-то условия:

IF lExpression ELSE
ENDIF

lExpression любое логическое выражение. Если оно истинно, то выполняется «часть кода 1», иначе «часть кода 2». В VFP существует масса функций, результат которых является логическим и поэтому может использоваться в этой конструкции.

Циклы позволяют выполнить какую либо часть кода несколько раз. Соответственно, в VFP существуют несколько видов циклов:

Циклы с предусловием
Циклы перечисления
Циклы «для каждого»

Циклов с послесловием, таких как Паскалевский цикл repeat until, в VFP нет. Но, при желании, их можно легко реализовать. Циклы с предусловием это циклы, выполняющиеся до тех пор, пока условие выполнения истинно. Проверка условия происходит перед очередным циклом, отсюда и название.

Такой цикл строится такой конструкцией :

DO WHILE lExpression

ENDDO

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

Типичный цикл перечисления:

FOR I = nMin TO nMax STEP nStep

ENDFOR

В этом случае, происходит последовательное увеличение переменной цикла (в данном примере I) от минимального значения nMin, до максимального nMax, с шагом приращения nStep.

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

Приведем два примера использования этого цикла. Первый пример создается массив и затем последовательно выводятся его элементы оператором (?) :

DIMENSION cMyArray(3)
cMyArray[1] = ‘A’
cMyArray[2] = ‘B’
cMyArray[3] = ‘C’
FOR EACH cMyVar IN cMyArray
? cMyVar
ENDFOR

В следующем примере с помощью OLE Automation создается объект книги Excel и последовательно выводятся имена листов этой книги:

oExcel = CREATE(«Excel.Application»)
oExcel.Workbooks.ADD
FOR EACH oMyVar IN oExcel.sheets
? oMyVar.name
ENDFOR

Во всех циклах можно прервать выполнение цикла оператором EXIT или прекратить выполнение текущей итерации (перейти на следующую итерацию) с помощью команды LOOP.