воскресенье, 21 мая 2017 г.

Переменные и типы данных LibreOffice Basic

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


Соглашения об именовании переменных

Имена переменных не могут включать более 255 символов. Любое имя переменной должно начинаться с заглавной или строчной буквы латинского алфавита или знака подчеркивания ("_"). В именах переменных разрешается использовать цифры и знак подчеркивания. Некоторые другие знаки препинания и буквы не латинского алфавита приведут к появлению сообщения "Синтаксическая ошибка" или "Ошибка выполнения BASIC", если их не заключить в квадратные скобки.
Примеры правильного наименования переменных.
MyNumber=5
MyNumber5=15
MyNumber_5=20
[My Number]=20.5
[5MyNumber]=12
[Number,Mine]=12
[DéjàVu]="кажется я это видел!"
[Моя переменная]="первый пошёл!"
[Мой % от сделки]=0.0001
Обратите внимание на квадратные скобки. Если их убрать, то макрос начнет выдавать ошибку. Как вы можете догадаться, этот способ позволяет нам задавать переменные с кириллическими именами. Но для того, чтобы писать также как в 1С только русскими командами, вам придется сделать довольно толстый слой абстракции. Помните, что все служебные слова, встроенные объекты, их методы и свойства в языке LibreOffice Basic в настоящее время пишутся латинским алфавитом. Удобно ли пользоваться квадратными скобками, решать вам. Возможность задавать экзотические имена переменным существует, но на практике в нашем сообществе этот функционал используется исключительно редко.

Объявление переменных

Строго говоря в Basic объявлять переменные не обязательно, за исключением массивов. Если вы пишите макрос из пары строчек, и этот макрос будет работать с небольшими документами, то объявлением переменных можно пренебречь. В этом случае переменная будет автоматически объявляться как тип Variant. Во всех случаях когда макрос больше пары строчек или документ большой, настоятельно рекомендуется объявлять переменные. Во-первых, это увеличивает читаемость текста. Во-вторых, это позволяет контролировать переменные, что может значительно облегчить поиск ошибок. В-третьих, тип данных Variant очень ресурсозатратный, и необходимо значительное время для скрытого преобразования. Кроме того, Variant выбирает не оптимальный тип переменной для данных, что также увеличивает загруженность ресурсов компьютера.
Basic предоставляет возможность автоматического присвоения типа переменной по её префиксу (первой буквы в имени) для упрощения работы, если вы предпочитаете использовать венгерскую нотацию. Для этого используется ключевое слово DefXXX и после него буква, с которой начинаются переменные этого типа. XXX в данном случае буквенное обозначение типа. Ключевое слово с буквой будет работать в пределах модуля, и должно указываться до появления подпрограмм и функций. Всего существует 11 таких типов.

DefBool — для логический переменных;
DefInt — для целочисленных переменных типа Integer;
DefLng — для целочисленных переменных типа Long;
DefSng — для переменных с плавающей точкой одинарной точности типа Single;
DefDbl — для переменных с плавающей точкой двойной точности типа Double;
DefCur — для переменных с фиксированной точкой типа Currency;
DefStr — для строковых переменных;
DefDate — для переменных даты и времени;
DefVar — для переменных типа Variant;
DefObj — для объектных переменных;
DefErr — для объектных переменных содержащих информацию об ошибках.

Если вы уже имеете представление о типах переменных в LibreOffice Basic, то наверное заметили, что в этом списке нет типа Byte, но есть для странного зверя с типом Error. К сожалению, это нужно просто запомнить. Вменяемого ответа на этот вопрос я пока не нашёл. Этот способ удобен, так как тип присваивается переменным автоматически. Но он не позволяет находить ошибки, связанные с опечатками в переменных. Кроме того, не удастся задать буквы не латинского алфавита, то есть, все имена переменных в квадратных скобках, которые необходимо объявлять, нужно объявлять явно.
Для избежания случайных опечаток при использовании объявленных явно переменных можно воспользоваться инструкцией OPTION EXPLICIT. Эта инструкция должна быть первой строкой кода в модуле. Все остальные команды, кроме комментариев, должны располагаться после неё. Эта инструкция указывает интерпретатору, что все переменные должны быть объявлены явно, иначе он выдает ошибку. Естественно, эта инструкция делает бессмысленным использование инструкции Def в коде.
Объявить переменную можно с помощью оператора Dim. Можно объявлять несколько переменных одновременно даже разного типа, если разделять их имена запятыми. Для определения типа переменной при явном объявлении можно использовать либо соответствующее ключевое слово, либо знак типа после имени. Если после переменной не используется ключевое слово или знак типа переменной, то ей автоматически присваивается тип Variant. Например:
Dim iMyVar 'объявлена переменная типа Variant'
Dim iMyVar1 As Integer, iMyVar2 As Integer 'в обоих случаях тип целое'
Dim iMyVar3, iMyVar4 As Integer 'в этом случае первая переменная'
                                'Variant, а вторая целое'

Типы переменных

В LibreOffice Basic предусмотрена поддержка семи классов переменных.
  • Логические переменные содержат одно из значений: TRUE или FALSE.
  • Числовые переменные содержат числовые значения. Они могут быть целочисленные, целочисленные положительные, с плавающей точкой и с фиксированной точкой.
  • Строковые переменные содержат строки символов.
  • Переменные даты могут содержать дату и/или время во внутреннем формате.
  • Объектные переменные могут содержать объекты различных типов.
  • Специальные типы, такие как структуры, массивы.
  • Абстрактный тип Variant.

Логические переменные — тип Boolean

Переменные типа Boolean могут содержать только одно из двух значений: TRUE или FALSE. При этом нужно понимать, что в числовом эквиваленте значению FALSE соответствует число 0, а значению TRUE соответствует -1 (минус единица). Но любое значение отличное от нуля переданное в переменную типа Boolean будет конвертировано в TRUE, то есть преобразовано в минус единицу. Явно объявить переменную можно следующим способом.
Dim MyBoolVar As Boolean
Специального символа для неё я не нашёл. Для неявного объявления можно воспользоваться инструкцией DefBool. Например:
DefBool b 'переменные начинающиеся с b по умолчанию имеют тип Boolean
Начальное значение переменной устанавливается в FALSE. Для переменной типа Boolean требуется один байт памяти.

Целочисленные переменные

К целочисленным переменным относится 3 типа Byte, Integer и Long Integer. Эти переменные могут содержать только целые числа. При передаче чисел с дробной частью в такие переменные, происходит округление по правилам классической арифметики (а не в большую сторону, как написано в справке). Начальным значением для этих переменных является 0 (ноль).

Типа Byte

Переменные типа Byte могут содержать только целочисленные положительные значения в диапазоне от 0 до 255. Не путайте этот тип с физическим размером информации в байт. Слово Byte лишь указывает на размерность числа. Объявить переменную этого типа можно следующим образом:
Dim MyByteVar As Byte
Специального символа для объявления этого типа нет. Инструкции Def для этого типа тоже нет. Из-за маленькой размерности этот тип будет удобнее всего в счетчиках, значения которых не выходят за диапазон. Для переменной типа Byte требуется один байт памяти.

Тип Integer

Переменные типа Integer могут содержать целочисленные значение от -32768 до 32767. Они удобны для быстрых вычислений в целых числах и пригодны в счетчиках циклов. "%" — специальный символ объявления типа. Объявить переменную этого типа можно следующими способами:
Dim MyIntegerVar% 
Dim MyIntegerVar As Integer
Для неявного объявления можно воспользоваться инструкцией DefInt. Например:
DefInt i 'переменные начинающиеся с i по умолчанию имеют тип Integer
Для переменной типа Integer требуется два байта памяти.

Тип Long Integer

Переменные типа Long Integer могут содержать целочисленные значения от -2147483648 до 2147483647. Переменные типа Long Integer удобны в целочисленных вычислениях, когда диапазона типа Integer недостаточен для реализации алгоритма. "&" — специальный символ объявления типа. Объявить переменную этого типа можно следующими способами:
Dim MyLongVar&
Dim MyLongVar as Long
Для неявного объявления можно воспользоваться инструкцией DefLng . Например:
DefLng l 'переменные начинающиеся с l будут по умолчанию иметь тип Long
Для переменной типа Long Integer требуется четыре байта памяти.

Числа с дробной частью

Все переменные этого типа могут принимать положительные или отрицательные значения чисел с дробной частью. Начальным значением для них является 0 (ноль). Как говорилось выше, если число с дробной частью присвоено переменной способной содержать только целые числа, то LibreOffice Basic округляет число по правилам классической арифметики.

Тип Single

Переменные типа Single могут принимать положительные или отрицательные значения в диапазоне от 3.402823x10E+38 до 1.401293x10E-38. Значения переменных этого типа — это числа с плавающей точкой одинарной точности и могут иметь 7 знаков после запятой. Если объяснять на пальцах, в этом формате хранятся только 8 знаков числа (мантисса числа), а остальная часть храниться виде степени десяти (порядок числа). В отладчике Basic IDE можно видеть только 6 знаков после запятой, но это наглая ложь. Вычисления с переменными типа Single занимают больше времени, чем для переменных типа Integer, но выполняются быстрее, чем вычисления с переменными типа Double. Специальным символом объявления типа является "!". Объявить переменную этого типа можно следующими способами:
Dim MySingleVar!
Dim MySingleVar as Single
Для неявного объявления можно воспользоваться инструкцией DefSng . Например:
DefSng f 'переменные начинающиеся с f по умолчанию имеют тип  Single
Для переменной типа Single требуется 4 байта памяти.

Тип Double

Переменные типа Double могут принимать положительные или отрицательные значения в диапазоне от 1.79769313486231598x10E308 до 1.0x10E-307. Почему такой странный диапазон? Скорее всего в интерпретаторе есть дополнительные проверки, которые приводят к такой странной ситуации. Значения переменных типа Double — это числа с плавающей точкой двойной точности и могут иметь 15 знаков после запятой. В отладчике Basic IDE можно видеть только 14 знаков после запятой, но это тоже наглая ложь. Переменные типа Double пригодны для точных вычислений. Вычисления требуют больше времени, чем вычисления для типа Single. Специальным символом объявления типа является "#". Объявить переменную этого типа можно следующими способами:
Dim MyDoubleVar#
Dim MyDoubleVar As Double
Для неявного объявления можно воспользоваться инструкцией DefDbl . Например:
DefDbl d 'переменные начинающиеся с d по умолчанию имеют тип  Double
Для переменной типа Double требуется 8 байта памяти.

Тип Currency

Переменные типа Currency отображаются как числа с фиксированной точкой и имеют 15 знаков в целой части и 4 знака в дробной. Диапазон значений включает числа от -922337203685477.6874 до +922337203685477.6874. Переменные типа Currency предназначены для точных расчетов денежных значений. Специальным символом описания типа является "@". Объявить переменную этого типа можно следующими способами:
Dim MyCurrencyVar@
Dim MyCurrencyVar As Currency
Для неявного объявления можно воспользоваться инструкцией DefCur . Например:
DefCur c 'переменные начинающиеся с c по умолчанию имеют тип Currency
Для переменной типа Currency требуется 8 байт памяти.

Строковые переменные — тип String

Переменные типа String могут содержать строки , в которых каждый не более 65535 символов. Каждый символ хранится как соответствующее значение Юникод. Они используются для работы с текстовой информацией и, кроме печатных знаков (символов), могут также содержать непечатаемые знаки. О максимальном размере строки я не знаю. Mike Kaganski экспериментальным образом установил значение в 2147483638 символа, после чего LibreOffice падает. Это соответствует почти 4 Гигабайтам символов.Специальным символом описания типа является "$". Объявить переменную этого типа можно следующими способами:
DimMyStringVar$
Dim MyStringVar As String
Для неявного объявления можно воспользоваться инструкцией DefStr . Например:
DefStr s 'переменные начинающиеся с s по умолчанию имеют тип String
Начальное значение этих переменных пустая строка (""). Память, требуемая для хранения строковых переменных, зависит от числа символов в переменной.

Даты и время — типа Date

Переменные типа Date могут содержать только значения даты и времени, сохраненные во внутреннем формате. Этот внутренний формат является по факту числом с плавающей точкой двойной точности Double, где целая часть это количество дней, а дробная часть дня (то есть, 0.00001157407 — это одна секунда). При этом значение 0 равно 30.12.1899. Но интерпретатор Basic автоматически конвертирует его в читабельный вариант при выводе, но не при вводе. Для правильного и быстрого преобразования во внутренний формат типа Date можно использовать функции Dateserial, Datevalue, Timeserial или Timevalue. Извлечь какую-нибудь определённую часть из переменной в формате Date можно с помощью функции Day, Month, Year или Hour, Minute, Second. Внутренний формат позволяет сравнивать значения даты и времени путем расчета разности двух чисел. Для типа Date нет специального символа для определения, поэтому при явном определении нужно использовать ключевое слово Date.
Dim MyDateVar As Date
Для неявного объявления можно воспользоваться инструкцией DefDate . Например:
DefDate y 'переменные начинающиеся с y по умолчанию имеют тип Date
Для переменной типа Date требуется 8 байта памяти.

Тип переменных Object

Условно к объектам в LibreOffice Basic можно отнести два типа переменных.

Объекты

Переменные типа Object являются переменными хранящими объекты. Если не вдаваться в подробности, то объектом считается любая обособленная часть программы имеющая структуру, свойства и методы доступа и обработки данных. Например, документ, ячейка, параграф, диалоговые окна — это объекты. Они имеют имя, размер, свойства, и методы их задания. В свою очередь, эти объекты состоят тоже из объектов, которые в свою очередь тоже могут состоять из объектов. Такая пирамида объектов часто называется объектной моделью, и она позволяет, разрабатывая маленькие объекты, объединять их в большие. В свою очередь, через больший объект, мы имеем доступ к более мелким. И это позволяет нам оперировать нашими документами, заниматься созданием и обработкой их, при этом абстрагировавшись от конкретного документа. Для типа Object нет специального символа для определения, поэтому при явном определении нужно использовать ключевое слово Object.
Dim MyObjectVar As Object
Для неявного объявления можно воспользоваться инструкцией DefObj . Например:
DefObj o 'переменные начинающиеся с o по умолчанию имеют тип Object
Переменная типа Object не хранит в себе сам объект, а является только ссылкой на него. Начальное значение для этого типа переменных Null.

Структуры

Структура по сути тоже объект. И если вы будете смотреть в отладчике Basic IDE, то у большинства из них вы увидите тип Object. Не у всех. У некоторых, например, как у структуры Error будет тип Error, а не Object. Но грубо говоря структуры в LibreOffice Basic это просто сгруппированные в один объект переменные, без специальных методов доступа. Ещё одним существенным отличием является то, что при объявлении переменной типа "структура" мы должны указывать её имя, а не служебное слово Object. Например, если MyNewStructure это имя структуры, то объявление её переменной будет выглядеть как:
Dim MyStructureVar As MyNewStructure
Существует довольно большое количество встроенных структур. Но пользователь может создавать и свои личные. Структуры могут быть удобны в тех случаях когда нам нужно оперировать наборами разнородной информации, которые должны рассматриваться как единое целое. Например, имя, фамилия и отчество человека. В этом случае мы могли бы, например, создать структуру tPerson.
Type tPerson
  Name As String
  Age As Integer
  Weight As Double
End Type
Определение структуры должно идти до подпрограмм и функций её использующих.
Чтобы заполнить структуру можно воспользоваться следующим способом. Для примера, встроенная структура com.sun.star.beans.PropertyValue:
Dim oProp As New
 com.sun.star.beans.PropertyValue
 oProp.Name  = "Age"  'Устанавливаем свойство Name'
 oProp.Value = "Amy Boyer" 'Устанавливаем свойство Property'
Для более простого заполнения структуры, можно использовать оператор With.
Dim oProp As New com.sun.star.beans.PropertyValue
 With oProp
   .Name  = "Age"       'Устанавливаем свойство Name'
   .Value = "Amy Boyer" 'Устанавливаем свойство Property'
 End With
Начальное значение есть только для каждой переменной в структуре и соответствует типу переменной.

Тип переменных Variant

Это виртуальный тип переменных. Тип переменной автоматически подбирается под данные, которыми нужно оперировать. Проблема лишь в том, что для интерпретатора не стоит цели экономить наши ресурсы, и он предлагает не самые оптимальные варианты типов переменных. Например, он не знает что единицу можно записать в Byte, а 100000 в Long Integer, хотя воспроизводит тип, если значение передается из другой переменной с объявленным типом. И кроме того, само преобразование довольно ресурсозатратное. Поэтому этот тип переменных самый медленный из всех. Если необходимо объявить этот вид переменных, можно воспользоваться ключевым словом Variant. Но можно опустить описание типа вовсе, типа Variant будет присвоен автоматически. Специального знака для этого типа нет.
Dim MyVariantVar
Dim MyVariantVar As Variant
Для неявного объявления можно воспользоваться инструкцией DefVar . Например:
DefVar v 'переменные начинающиеся с v по умолчанию имеют тип Variant
Этот тип переменных присваивается по умолчанию всем не объявленным переменным.

Массивы

Массивы — это особый тип переменных в виде набора данных больше напоминающий математическую матрицу, за исключением того, что данные могут быть различного типа, и позволяющие обращаться к своим элементам по индексу (номеру элемента). Конечно, одномерный массив будет подобен столбцу или стрке, а двухмерный таблице. Есть одна особенность массивов в LibreOffice Basic, отличающая его от других языков программирования. Так как у нас есть абстрактный тип Variant, то элементы массива, не обязаны быть однородными. То есть, если у нас есть массив Array с тремя элементами под номерами от 0 до 2, и мы в первый элемент Array(0) записываем имя, во второй Array(1) возраст, а в третий Array(2) вес человека, то можем иметь соответственно значения типов для Array(0) String, для Array(1) Integer, а для Array(2) Double. В этом случае массив будет напоминать структуру с возможностью доступа к элементу по его индексу. Элементы массива также могут быть однородными, другими массивами, объектами, структурами, строками, или любым другим типом данных используемым в языке LibreOffice Basic.
Массивы обязательно должны быть объявлены. Хотя пространство индексов может быть в диапазоне типа Integer, то есть от -32768 до 32767, по умолчанию начальный индекс выбирается как 0. Объявить массив можно несколькими способами:
Dim MyArrayVar(5) as String         'Массив строк с 6 элементами от 0 до 5'
Dim MyArrayVar$(5)                  'тоже самое что и предыдущий вариант'
Dim MyArrayVar(1 To 5) As String    'Массив строк с 5 элементами от 1 до 5'
Dim MyArrayVar(5,5) As String       'двухмерный массив строк с 36 элементами'
                                    'с индексами в каждом уровне от 0 до 5'
Dim MyArrayVar$(-4 To 5, -4 To 5)   'двухмерный массив строк со 100'
                                    'элементами с индексами в каждом уровне'
                                    'от -4 до 5'
Dim MyArrayVar()                    'пустой массив типа Variant'
Изменить нижнюю границу (индекс первого элемента массива) по умолчанию можно при помощи ключевых слов Option Base, которые должны быть указаны до использования подпрограмм, функций и определения пользовательский структур. Option Base может принимать только два значения 0 или 1, которые должны следовать сразу после ключевых слов. Действие распространяется только на текущий модуль.

Дополнительная литература и источники: