2. Определение языка
Все объекты, содержащие данные, в Euphoria могут быть атомами или рядами. Атом это единственная численная величина. Ряд является набором численных величин. Объекты, входящие в ряд, могут быть произвольной смесью атомов или рядов. Ряд представляется списком объектов, заключенным в фигурные скобки, объекты разделяются запятыми. Атомы могут иметь любую целочисленную величину или величину двойной точности с плавающей точкой. Диапазон значений величин лежит в пределах от приблизительно -1e300 (минус единица на 10 в степени 300) до +1e300 с 15-ю десятичными цифрами точности. Ниже приводятся примеры некоторых объектов Euphoria: -- примеры атомов: 0 1000 98.6 -1e6 -- примеры рядов: {2, 3, 5, 7, 11, 13, 17, 19} {1, 2, {3, 3, 3}, 4, {5, {6}}} {{"jon", "smith"}, 52389, 97.25} {} -- пустой ряд, 0 элементов Числа могут быть также шестнадцатиричными, например: #FE -- 254 #A000 -- 40960 #FFFF00008 -- 68718428168 -#10 -- -16
Для написания шестнадцатиричных чисел разрешены только
прописные буквы Ряды могут быть вложенными на любую глубину, т.е. вы можете иметь ряды внутри ряда рядов и так далее на любую глубину, пока не выйдете за пределы доступной памяти на своей машине. Фигурные скобки используются также для конструирования рядов из списка выражений. Эти выражения могут быть константами или вычисляться во время прогона, т.е. {x+6, 9, y*w+2, sin(0.5)} Слова "Hierarchical Objects", часть акронима Euphoria, отражают иерархическую природу вложенных рядов. Здесь не должно быть путаницы с "class hierarchies" некоторых объектно-ориентированных языков. Почему мы назвали их атомами? Почему не просто "числами"? Что ж, атомы действительно являются просто числами, но мы хотели подчеркнуть их дальнейшую неделимость в ряд. Конечно же, в мире физики атомы давным-давно разделены на более мелкие части, но в Euphoria вы не можете поделить атом в ряд. Это основной строительный материал, кирпичик, для создания всех типов данных, которыми программа Euphoria может манипулировать. Следуя этой аналогии, ряды можно представить как "молекулы", собранные из атомов и других молекул. Лучшей же аналогией будет представление о рядах как о каталогах, а об атомах как о файлах. Точно так же, как каталог на вашем компьютере может содержать и файлы, и другие каталоги, ряд может содержать и атомы, и другие ряды (а эти другие ряды могут содержать и атомы, и ряды и так далее). Как вы скоро убедитесь, именно ряды делают Euphoria очень простым и очень мощным языком. Понимание атомов и рядов является ключом к пониманию Euphoria.
2.1.2 Строки символов и отдельные символы Строка символов является просто рядом символов. Она может быть введена с использованием кавычек, т.е. "ABCDEFG" Строки символов обрабатываются точно так же, как и все другие ряды. Например, строка выше полностью эквивалентна ряду: {65, 66, 67, 68, 69, 70, 71}который содержит соответствующие коды ASCII. Компилятор Euphoria немедленно преобразует "ABCDEFG" в этот эквивалентный ряд чисел. Другими словами, в Euphoria нет символьных строк, а есть только ряды чисел. Строка в кавычках на самом деле это просто удобная вторая форма синтаксиса для ряда символов, которая избавляет вас от необходимости набирать коды ASCII вместо обычной печати текста на клавиатуре. Следовательно, "" эквивалентно {}. Оба обозначения представляют ряд длины-0, известный также как пустой ряд. Что касается стиля программирования, более естественно писать "" для выражения ряда длины-0, не содержащего символов, и {} для выражения аналогичных всех других пустых рядов. Отдельный символ является атомом. Он вводится с использованием одинарных кавычек. Такая форма записи позволяет различать отдельный символ (который является атомом), и строку символов длины-1 (которая является рядом) т.е. 'B' -- эквивалентно атому 66 - код ASCII для B "B" -- эквивалентно ряду {66} Повторим, 'B' есть просто форма, эквивалентная вводу 66. В Euphoria на самом деле вовсе нет никаких "символов", есть только числа (атомы). Необходимо твердо усвоить, что атом не является эквивалентом одноэлементного ряда, содержащего ту же самую величину, хотя в интерпретаторе и есть несколько встроенных подпрограмм, которые обходятся с ними сходным образом. Специальные символы могут быть введены с помощью обратной косой черты (обратного солидуса): \n новая строка \r возврат каретки \t табуляция \\ обратный солидус \" двойная кавычка \' одинарная кавычка Например, "Hello, World!\n", или '\\'. Редактор Euphoria отображает символьные строки в зеленом цвете (цвета можно изменять). Комментарии начинаются с двух дефисов и продолжаются до конца текущей строки, т.е. -- это комментарий Комментарии игнорируются компилятором и не влияют на скорость прогона программы. Редактор отображает комментарии в красном цвете. В первой (только) строке программы вы можете использовать специальный комментарий, начинающийся с #!, т.е. #!/home/rob/euphoria/bin/exuЭта строка информирует Linux, что ваш файл должен исполняться интерпретатором Euphoria, и дает полный путь к этому интерпретатору. Если вы делаете свой файл исполняемым, вы можете запускать его, просто введя его имя, без необходимости ввода "exu". Под DOS и Windows эта первая строка обрабатывается как комментарий.
2.2 Выражения Подобно другим языкам программирования, Euphoria позволяет вам производить вычисления, записывая выражения для результатов. Более того, Euphoria позволяет вам выполнять вычисления сразу с целыми рядами в одном выражении, в то время как в большинстве других языков вам для подобных вычислений прийдется конструировать цикл. В Euphoria вы можете обходиться с рядами так же, как с отдельными числами. Ряды могут быть скопированы, поданы в подпрограмму или вычислены как единое целое. Например, {1,2,3} + 5является выражением, в котором складываются ряд {1,2,3} и атом 5, чтобы получить в результате ряд {6,7,8}. Чуть позже мы сможем увидеть другие примеры действий с рядами. Операторы отношения < > <= >= = != все дают в результате 1 (истина) или 0 (ложь). 8.8 < 8.7 -- 8.8 меньше чем 8.7 (ложь) -4.4 > -4.3 -- -4.4 больше чем -4.3 (ложь) 8 <= 7 -- 8 меньше или равно 7 (ложь) 4 >= 4 -- 4 больше или равно 4 (истина) 1 = 10 -- 1 равно 10 (ложь) 8.7 != 8.8 -- 8.7 не равно 8.8 (истина) Как мы скоро увидим, вы можете применять эти операторы и к рядам. Логические операторы and, or, xor, и not используются, чтобы задать "истинность" выражения т.е. 1 and 1 -- 1 (истина) 1 and 0 -- 0 (ложь) 0 and 1 -- 0 (ложь) 0 and 0 -- 0 (ложь) 1 or 1 -- 1 (истина) 1 or 0 -- 1 (истина) 0 or 1 -- 1 (истина) 0 or 0 -- 0 (ложь) 1 xor 1 -- 0 (ложь) 1 xor 0 -- 1 (истина) 0 xor 1 -- 1 (истина) 0 xor 0 -- 0 (ложь) not 1 -- 0 (ложь) not 0 -- 1 (истина) Вы можете применять эти операторы к числам, отличающимся от 1 или 0. Правило таково: нуль означает ложь, не-нуль означает истину. Итак, например: 5 and -4 -- 1 (истина) not 6 -- 0 (ложь) Данные операторы могут применяться к рядам. См.ниже. В некоторых случаях для вычисления логических выражений, содержащих and или or, применяется укороченная процедура.
Доступны обычные арифметические операторы: сложение, вычитание, умножение, деление, унарный минус, унарный плюс. 3.5 + 3 -- 6.5 3 - 5 -- -2 6 * 2 -- 12 7 / 2 -- 3.5 -8.1 -- -8.1 +8 -- +8 Вычисление результата, который слишком велик (т.е. находится вне пределов от -1e300 до +1e300), будет давать один из специальных атомов +бесконечность или -бесконечность. Эти результаты будут отображаться как inf или -inf, когда вы станете их выводить на экран или в файл. Возможно также появление результатов nan или -nan. "nan" означает "not a number - не число", т.е. неопределенная величина (такая как inf деленное на inf). Эти специальные величины введены стандартом IEEE о вычислениях с плавающей точкой. Если вы встретите одну из этих специальных величин среди своих результатов, обычно это будет свидетельствовать об ошибках в логике вашей программы, хотя в некоторых случаях inf может быть промежуточным результатом, вполне приемлемым и преднамеренным. Например, 1/inf дает 0, который может быть "правильным" ответом в вашем алгоритме. Деление на нуль, так же как подача недопустимых параметров в подпрограммы математической библиотеки, т.е. квадратный корень из отрицательного числа, логарифм неположительного числа и т.д. вызывают немедленное сообщение об ошибке с аварийным завершением вашей программы (авост'с!). Единственным поводом для употребления унарного плюса может быть необходимость привлечения внимания читателя вашей программы или вашего пользователя, что величина именно положительная, а никак не отрицательная. Интерпретатор же не делает с ним ровно ничего. Все операторы отношения, логические и арифметические операторы, описанные выше, а равно и математические подпрограммы, описанные в Часть II - Библиотечные подпрограммы, могут быть применены к рядам, так же как к отдельным числам (атомам). Будучи примененным к ряду, унарный (однооперандный) оператор применяется к каждому из элементов ряда, чтобы получить ряд результатов той же самой длины. Если один из элементов ряда является рядом, то же самое правило применяется снова рекурсивно, т.е. x = -{1, 2, 3, {4, 5}} -- x равно {-1, -2, -3, {-4, -5}} Если бинарный (двухоперандный) оператор применяется с операндами, каждый из которых является рядом, оба эти ряда должны быть одной и той же длины. Бинарная операция применяется к соответствующим элементам, взятым из обоих рядов, чтобы получить ряд результатов, т.е. x = {5, 6, 7, 8} + {10, 10, 20, 100} -- x равно {15, 16, 27, 108} Если бинарный оператор применяется с операндами, один из которых является рядом, тогда как второй является одиночным числом (атомом), одиночное число повторяется интерпретатором в форму ряда, соответствующего первому операнду. Затем применяются правила действий с двумя рядами одинаковой длины. Несколько примеров: y = {4, 5, 6} w = 5 * y -- w равно {20, 25, 30} x = {1, 2, 3} z = x + y -- z равно {5, 7, 9} z = x < y -- z равно {1, 1, 1} w = {{1, 2}, {3, 4}, {5}} w = w * y -- w равно {{4, 8}, {15, 20}, {30}} w = {1, 0, 0, 1} and {1, 1, 1, 0} -- {1, 0, 0, 0} w = not {1, 5, -2, 0, 0} -- w равно {0, 0, 0, 1, 1} w = {1, 2, 3} = {1, 2, 4} -- w равно {1, 1, 0} -- обратите внимание, что первое '=' есть присваивание, а -- второе '=' есть оператор отношения, который проверяет -- эквивалентность 2.2.5 Индексирование рядов Единственный элемент ряда может быть отобран путем задания номера элемента в квадратных скобках. Номера элементов (индексы) начинаются с 1. Нецелые индексы округляются вниз до ближайшего целого, т.е. отбрасывается дробная часть нецелого индекса. Например, если x содержит {5, 7.2, 9, 0.5, 13}, тогда x[2] равно 7.2. Предположим, мы присвоили другое значение x[2]: x[2] = {11,22,33} Тогда x становится: {5, {11,22,33}, 9, 0.5, 13}. Если мы теперь возьмем x[2], то получим {11,22,33}, а если мы возьмем x[2][3], то получим атом 33. Если вы попытаетесь индексировать с числом, которое выходит за пределы диапазона от 1 до числа элементов, вы получите сообщение об ошибке индексирования. Например, x[0], x[-99] или x[6] вызовут сообщение об ошибке. То же самое будет в случае x[1][3], так как x[1] не является рядом. Предел на число индексов, которыми вы можете снабдить переменную, не установлен, но чтобы не было ошибок индексирования, переменная должна иметь соответствующую числу индексов глубину вложения рядов. Двухмерный массив, широко используемый в других языках, может быть легко представлен рядом рядов: x = { {5, 6, 7, 8, 9}, -- x[1] {1, 2, 3, 4, 5}, -- x[2] {0, 1, 0, 1, 0} -- x[3] }Желательно разместить числа так, чтобы сделать структуру более ясной. Для доступа к необходимому элементу может быть применено выражение в форме x[i][j]. Тем не менее, два измерения здесь не симметричны в том смысле, что полная "строка" может быть отобрана как x[i], но такое же простое выражение не существует для полной "колонки". Другие логические структуры, такие как n-мерные массивы, массивы строк, структуры, массивы структур и т.п. "штучки" могут быть заданы и обработаны легко и гибко: 3-D массив: y = { {{1,1}, {3,3}, {5,5}}, {{0,0}, {0,1}, {9,1}}, {{-1,9},{1,1}, {2,2}} } y[2][3][1] равно 9 Массив строк: s = {"Hello", "World", "Euphoria", "", "Last One"} s[3] равно "Euphoria" Структура: employee = { {"John","Smith"}, 45000, 27, 185.5 } Для доступа к "полям" или элементам внутри структуры в хорошем программистском стиле рекомендуется иметь набор поименованных констант, которые обозначали бы поля осмысленными названиями. Этот прием сделает вашу программу легкой для чтения и понимания. Для примера выше вы могли бы иметь: constant NAME = 1 constant FIRST_NAME = 1, LAST_NAME = 2 constant SALARY = 2 constant AGE = 3 constant WEIGHT = 4 Затем вы могли бы вызвать поле с именами людей так - employee[NAME], или, если вас итересует фамилия - employee[NAME][LAST_NAME]. Массив структур: employees = { {{"John","Smith"}, 45000, 27, 185.5}, -- a[1] {{"Bill","Jones"}, 57000, 48, 177.2}, -- a[2] -- и т.д. } employees[2][SALARY] будет 57000. Структуры данных Euphoria имеют почти бесконечную гибкость. Массивы в других языках объявляются с постоянным числом элементов, и эти элементы должны все быть одного и того же типа. Euphoria снимает оба эти ограничения. Вы можете добавить новую структуру в ряд employee выше или разместить необычайно длинное имя в поле NAME и Euphoria позаботится обо всем остальном вместо вас. Если вы хотите, вы можете разместить сонм различных "структур" вроде employee, все с различными размерностями, и все в одном ряде. Программа Euphoria может не только легко представлять все обычные структуры данных, но и дает вам возможность создавать очень полезные, гибкие структуры, которые было бы экстремально трудно объявить в обычных языках. Прочитайте раздел 2.3 Euphoria против обычных языков. Имейте в виду, что выражения в общем случае не могут индексироваться, только переменные. Например: {5+2,6-1,7*8,8+1}[3] не поддерживается, равно как и что-нибудь вроде: date()[MONTH]. Вы должны присвоить значение ряда, выданного date(), переменной, а затем проиндексировать переменную, чтобы получить значение необходимого вам элемента ряда. Из ряда может быть отобран (отсечен, выделен) более короткий ряд последовательных элементов путем задания номеров начального и конечного элементов. Например, если x равно {1, 1, 2, 2, 2, 1, 1, 1}, тогда x[3..5] является рядом {2, 2, 2}. x[3..3] есть ряд {2}. x[3..2] также разрешено, эта форма выдает ряд длины-0, {}. Если y равно {"fred", "george", "mary"}, тогда y[1..2] равно {"fred", "george"}. Сечение может использоваться для переписывания участков переменных. Если мы выполним x[3..5] = {9, 9, 9}, x станет равным {1, 1, 9, 9, 9, 1, 1, 1}. С тем же эффектом можно написать x[3..5] = 9. Предположим, y равно {0, "Euphoria", 1, 1}. Тогда y[2][1..4] равно "Euph". Если мы напишем y[2][1..4]="ABCD", тогда y станет равным {0, "ABCDoria", 1, 1}. В общем случае, имя переменной может сопровождаться 0 или более индексов, которые завершаются 0 или 1 отрезком. Только переменные допускают индексирование и сечение, но не выражения. Необходимо внести побольше точности в отношении правил, действующих для пустых отрезков. Представим себе отрезок s[i..j], где s имеет длину n. Отрезок от i до j, где j = i-1 и i >= 1 производит пустой ряд, даже если i = n+1. Таким образом, 1..0, и n+1..n, и все, что лежит между этими крайними случаями, дает законные (пустые) отрезки. Пустые отрезки необыкновенно полезны во многих алгоритмах. Отрезки от i до j, где j < i - 1 незаконны, т.е. "вывернутые" отрезки, такие как s[5..3], не разрешены.
Любые два объекта могут быть сцеплены с применением оператора &. Результатом будет ряд с длиной, эквивалентной сумме длин сцепленных объектов (здесь атомы считаются имеющими длину 1), т.е. {1, 2, 3} & 4 -- {1, 2, 3, 4} 4 & 5 -- {4, 5} {{1, 1}, 2, 3} & {4, 5} -- {{1, 1}, 2, 3, 4, 5} x = {} y = {1, 2} y = y & x -- y все еще равен {1, 2} Вы можете удалять элемент i из любого ряда s путем сцепления (иначе, конкатенации) частей ряда до и после элемента i: s = s[1..i-1] & s[i+1..length(s)]Этот прием работает, даже если i равно 1 или length(s), так как [1..0] является законным пустым отрезком, равно как и s[length(s)+1..length(s)]. Наконец, формирование ряда с использованием фигурных скобок и запятых: {a, b, c, ... }также является оператором. Этот оператор берет n операндов, где n равно 0 или более, и создает n-элементный ряд из их величин, т.е. x = {apple, orange*2, {1,2,3}, 99/4+foobar} Оператор формирования ряда находится в самом низу таблицы приоритетов.
Некоторые другие важные операции, которые вы можете выполнять на рядах, имеют английские имена, а не вид специальных символов. Эти операции встроены в ex.exe/ exw.exe/ exu, так что они всегда под рукой и они очень быстрые. Детальное их описание вы найдете в Часть II - Библиотечные подпрограммы, но для знакомства с программированием на Euphoria важно, чтобы вы получили о них представление уже сейчас, прежде чем мы продолжим о других вопросах. Вы обращаетесь к этим операциям так, как будто они являются подпрограммами, хотя на деле они работают значительно более эффективно, чем подпрограммы. length() length() выдает вам длину ряда s. Длина это число элементов в s. Некоторые из этих элементов могут быть рядами, которые содержат свои собственные элементы, но length дает подсчет только для самого верхнего уровня. Если вы попытаетесь определить длину атома, это вызовет сообщение об ошибке, т.е. length({5,6,7}) -- 3 length({1, {5,5,5}, 2, 3}) -- 4 (не 6!) length({}) -- 0 length(5) -- ошибка! repeat(item, count) repeat() соэдает ряд, который состоит из элемента item, повторенного count раз, т.е. repeat(0, 100) -- {0,0,0,...,0} т.е. 100 нулей repeat("Hello", 3) -- {"Hello", "Hello", "Hello"} repeat(99,0) -- {} Элемент, который должен повторяться, может быть любым атомом или рядом.
append() создает новый ряд, добавляя элемент item к концу ряда s. prepend() создает новый ряд, прибавляя элемент перед началом исходного ряда s, т.е. append({1,2,3}, 4) -- {1,2,3,4} prepend({1,2,3}, 4) -- {4,1,2,3} append({1,2,3}, {5,5,5}) -- {1,2,3,{5,5,5}} prepend({}, 9) -- {9} append({}, 9) -- {9} Длина нового ряда после выполнения этих операций всегда на 1 больше длины исходного ряда. Новый элемент может быть любым атомом или рядом. Эти две встроенные функции, append() и prepend(), имеют сходство с оператором сцепления (конкатенации), &, но имеются и ясные отличия, а именно : -- добавление ряда отличается append({1,2,3}, {5,5,5}) -- {1,2,3,{5,5,5}} {1,2,3} & {5,5,5} -- {1,2,3,5,5,5} -- добавление атома совпадает append({1,2,3}, 5) -- {1,2,3,5} {1,2,3} & 5 -- {1,2,3,5} 2.2.10 Таблица приоритетов Приоритеты при выполнении операторов и вычислении выражений следующие: высший приоритет: вызов функции/типа unary- unary+ not * / + - & < > <= >= = != and or xor низший приоритет: { , , , } Таким образом, 2+6*3 означает 2+(6*3), а вовсе не (2+6)*3. Операторы из одной строки таблицы имеют равные приоритеты и выполняются слева направо. Символ равенства '=', используемый как оператор присваивания, не является оператором, это всего лишь один из элементов синтаксиса языка.
2.3 Euphoria против обычных языков Построив Euphoria на единой простой общей рекурсивной структуре данных, удалось избежать громадной доли сложностей, обычно присутствующих в языках программирования. Массивы, структуры, союзы, массивы записей, многомерные массивы, и т.п. конструкции из других языков могут быть легко представлены в Euphoria рядами. Так же как и структуры более высокого уровня, такие как списки, стеки, очереди, деревья и т.д. Более того, в Euphoria вы можете иметь ряды смешанного типа; вы можете присвоить значение любого объекта элементу ряда; длина рядов легко увеличивается и уменьшается, не принося вам беспокойства о выделении памяти. Точное заблаговременное проектирование структуры данных не требуется, не требуется опережающее объявление структуры, она может динамически изменяться так, как это становится необходимым. Легко написать общий код, где, к примеру, вы будете использовать единственный стек для размещения и извлечения самых разнообразных смешанных типов данных. Создание гибкого списка, который содержит множество различных типов объектов, тривиально в Euphoria, но потребует десятков строк замысловатого до уродливости кода в других языках. Манипулирование структурами данных очень эффективно, так как интерпретатор Euphoria не копирует большие объекты, а обходится их пометками. Программирование в Euphoria полностью основано на создании гибкиx, подвижных рядов данных и манипулировании ими. Ряды и все - нет других структур данных, которые нужно было бы изучать. Вы оперируете в простом, безопасном, податливом мире величин, который далеко отстоит от жесткого, скучного, опасного мира битов, байтов, пойнтеров и машинных крахов. В отличие от других языков, таких как LISP и Smalltalk, Euphoria выполняет непрерывную "приборку" вышедших из использования блоков памяти, так что никогда не возникают случайные задержки в исполнении программы и не требуется предварительное резервирующее выделение громадных областей памяти. Определения обычных языков, таких как C, C++, Ada, и т.п., являются слишком сложными. Большинство программистов овладевает лишь более или менее полным подмножеством конструкций языка. Стандарты ANSI этих языков выглядят как фундаментальные своды законов. Вас принуждают писать различный код для различных типов данных, чтобы просто скопировать данные, запросить текущую длину, произвести их конкатенацию, сравнить их и т.п. Руководства по этим языкам переполнены процедурами вроде "strcpy", "strncpy", "memcpy", "strcat", "strlen", "strcmp", "memcmp", и т.д., которые работают только с одним из многих типов данных.
Многие сложности нагромождены вокруг вопроса о типе данных.
Как нужно определять новые типы? Какие типы данных могут быть смешанными?
Как преобразовать один тип в другой, да еще так, чтобы оставить
компилятор счастливым? В этих языках числовая величина 4 (к примеру) может иметь различный смысл в зависимости от того, что это - int, char, short, double, int, * и т.д. В Euphoria 4 есть атом 4, точка. В Euphoria тоже есть кое-что, названное типами, как мы увидим чуть позже, но это кое-что имеет намного более простую концепцию, чем в других языках. Вопросы динамического выделения и освобождения памяти занимают значительную долю рабочего времени программиста в тех других языках и делают результирующую программу намного менее понятной. Программы, которые должны исполняться непрерывно, часто испытывают утечку памяти, а это требует громадных усилий по поддержанию дисциплины безопасного и правильного освобождения блоков памяти, в которых отпадает необходимость. Широкое применение в других языках находят переменные-указатели (пойнтеры). Пойнтер напоминает оператор "goto" для ячеек памяти. Это принуждает программистов думать о данных так, как будто они представляют собой ячейки памяти разной длины, и манипулировать ими с помощью обширного набора низкоуровневых труднопереносимых методов, которые сродни искусству канатоходца. Картины из жизни внутренностей компьютера, на котором исполняется ваша программа, то и дело возникают в вашем воображении и преследуют вас, особенно, когда нужно найти ошибку. В Euphoria нет пойнтеров и она не нуждается в них, разве что для связи с функциями C.
2.4 Объявления
Идентификаторы,
которые включают в себя имена переменных и другие
определяемые программистом обозначения, могут быть любой длины. Верхний
и нижний регисты букв (строчные и прописные буквы) различаются
интерпретатором. Идентификаторы должны начинаться с буквы, а продолжаться
могут буквами, цифрами и знаками прочерка (подчерка). Следующие
служебные слова
имеют в Euphoria специальное предназначение и не могут
использоваться в качестве идентификаторов, определяемых программистом:
Редактор Euphoria отображает эти слова синим цветом. Идентификаторы применяются для наименования:
Процедуры выполняют заданные операции и могут иметь список параметров, т.е. procedure empty() end procedure procedure plot(integer x, integer y) position(x, y) puts(1, '*') end procedure Хотя выше вы можете видеть ограниченное число поименованных параметров, на самом деле это не является ограничением, так как любой параметр может быть рядом переменной длины, содержащим произвольные объекты. Во многих языках список параметров с переменной длиной просто невозможен. В C вы должны для получения такого списка установить странные механизмы, которые настолько сложны, что средний программист не сможет обойтись без штудирования документации или консультаций с местным гуру. В процедуру подается копия величины каждого из параметров. Эти копии являются формальными переменными со значением параметров и с их именем. Внутри процедуры они могут претерпевать изменения, но эти изменения не коснутся тех фактических исходных переменных, которые поданы как параметры (в отличие от QB4.5, например. Прим.перев.)
Функции Функции похожи на процедуры, но в отличие от них выдают величину и могут быть использованы в выражениях, т.е. function max(atom a, atom b) if a >= b then return a else return b end if end function Выдан же функцией может быть любой объект Euphoria. Вы можете, в сущности, иметь множество выдаваемых величин, выдавая ряд объектов, т.е. return {x_pos, y_pos} Мы используем общий термин "подпрограмма", когда сведения относятся и применимы и к процедурам, и к функциям. Типы это специальные функции, которые необходимы при задании допустимых диапазонов значений для переменных. Тип должен иметь строго один-единственный параметр и выдавать атом, который может быть либо истиной (не-нуль), либо ложью (нуль). Типы могут быть также вызваны подобно всем другим функциям. Если вас уже заинтересовали детали, прочитайте раздел 2.4.3 Задание типа переменной. Переменным во время исполнения программы могу быть присвоены величины, т.е. -- x могут быть присвоены только целочисленные величины integer x x = 25 -- a, b и c может быть присвоена *любая* величина object a, b, c a = {} b = a c = 0 Когда вы объявляете переменную, вы даете ей имя (которое защищает вас потом от ошибок правописания) и определяете величины, которые могут быть законно присвоены этой переменной во время исполнения вашей программы. Константы (постоянные) это переменные (!), которым присваиваются некоторые изначальные величины. Эти изначальные величины затем не могут быть изменены, т.е. constant MAX = 100 constant Upper = MAX - 10, Lower = 5 constant name_list = {"Fred", "George", "Larry"} Константе может быть присвоен результат вычисления любых выражений, даже значение единожды вызванной предварительно определенной функции, но будучи однажды присвоенной, величина константы "защелкивается" до конца прогона программы. Константы не могут быть объявлены внутри подпрограмм. Сценой называется участок программы, где объявленный идентификатор действует, то есть, где он является видимым. В Euphoria каждый идентификатор (имя) должен быть объявлен (определен) до его использования. Вы можете читать программу Euphoria от начала и до конца и вам никогда не встретится переменная или подпрограмма, которая не была бы прежде определена. Имеется, однако, возможность указать на подпрограмму, определение которой идет в исходном тексте позже. Но тогда вы, чтобы сделать этот трюк, должны после ее определения использовать специальную функцию, routine_id(), для которой имя этой подпрограммы является параметром, а для опережающего указания в определениях применять call_func() или call_proc() без упоминания имени, но используя переменную, которой позже будет присвоено значение идентификатора, выданного routine_id(). (См. Часть II - Библиотечные подпрограммы - Динамические вызовы). Процедуры, функции и типы могут вызывать сами себя рекурсивно. Взаимная рекурсия, когда подпрограмма A вызывает подпрограмму B, которая прямо или косвенно вызывает подпрограмму A, требует включения механизма routine_id(). Идентификатор считается определенным от точки, где он объявлен, до конца его сцены (области видимости). Сцена переменной, объявленной внутри процедуры или функции (такие переменные называются частными), заканчивается в конце процедуры или функции. Сцена всех других переменных, констант, процедур, функций и типов заканчивается в конце файла, в котором они были объявлены. Эти идентификаторы считаются локальными (местными), если при их объявлении было опущено ключевое слово global - глобальный, повсеместный. Если же слово global предшествует объявлению, сцена простирается от точки объявления до окончания вашего главного файла (программы). Когда вы включаете библиотечный файл Euphoria в свой главный файл, (см. 2.6 Специальные операторы верхнего уровня), только переменные и подпрограммы, объявленные с использованием ключевого слова global, оказываются доступными или хотя бы даже видимыми в вашем главном файле. Таким образом, не-global-ьные объявления во включаемых (include) файлах интерпретатор сразу забывает, как только заканчивает компиляцию .e-файла и его исполнение и возвращается в файл, вызвавший включение. Если вы попытаетесь обратиться к этим уже забытым идентификаторам изнутри главного файла, вы получите сообщение об ошибке, "not declared" - "не объявлено". Но в библиотечном файле (синоним для файла include) локальные идентификаторы видимы и доступны для глобальных и других подпрограмм этого библиотечного файла. Идентификаторы, помеченные как global, могут быть использованы вне своего файла. Все другие идентификаторы могут быть использованы только внутри своего собственного файла. Эта информация полезна при сопровождении или использовании файла, либо же при изучении возможностей его использования. Вы можете вносить изменения во внутренние подпрограммы и переменные, не заботясь о согласовании изменений с другими файлами или другими пользователями этого файла. Иногда при использовании библиотечных файлов, разработанных другими людьми, вы будете сталкиваться с конфликтами имен. Конфликт возникает, если один из авторов библиотечных файлов использовал те же самые имена, что и другой, а вы должны подключить обе библиотеки к своей программе. Если у вас есть исходный код библиотек, вы можете просто отредактировать одну из библиотек, чтобы изменить повторяющиеся имена. Но тогда вы должны будете повторять это редактирование с выходом каждой новой версии библиотеки, которая вам нужна. Euphoria предлагает простой путь для устранения конфликтов имен. С использованием расширенного оператора include вы можете сказать, например: include johns_file.e as john include bills_file.e as bill john:x += 1 bill:x += 2В этом случае переменная x была объявлена в двух различных файлах, а вы хотите обращаться к обеим переменным из своей программы. Используя идентификатор пространства имен, john или bill, вы можете снабдить x префиксом, чтобы показать, к какому именно x вы обратились. Мы иногда говорим, что john относится к одному пространству имен, а bill относится к другому отдельному пространству имен. Вы можете снабдить идентификатором пространства имен любую определенную программистом переменную, константу, процедуру или функцию. Вы можете делать это для устранения конфликта или просто для того, чтобы внести побольше ясности в свою программу. Идентификатор пространства имен имеет локальную (местную) сцену. Он известен только в пределах файла, где был определен, то есть, файла, содержащего расширенный оператор include. Другие файлы могут определять другие идентификаторы пространства имен, чтобы обращаться к тому же самому библиотечному файлу. Euphoria подталкивает программиста к сужению сцены идентификаторов. Если бы все идентификаторы были автоматически глобальными во всей программе, это порождало бы тенденцию к появлению конфликтов имен в больших программах, особенно, когда различные части программы создаются другими программистами. Конфликты в именах могут приводить к простым сообщениям об ошибке наименования во время компиляции, но они могут вести и к скрытой ошибке, когда различные части программы встречно модифицируют одну и ту же переменную, даже не давая программисту повода догадаться об этом. Старайтесь использовать такую узкую сцену для переменных, которая еще не становится обременительной. Делайте переменные частными для одной подпрограммы везде, где это возможно, а где это невозможно, делайте их местными для файла, прежде чем сделать global - общими для всей своей программы, начиная от точки объявления. Когда Euphoria проверяет объявление имени, она прежде всего смотрит в текущей подпрограмме, затем в текущем файле, затем среди глобальных имен в других файлах. Имена, которые в большей степени локализованы, будут иметь преимущество перед именами, которые в большей степени глобализованы. По окончании сцены локального имени глобальное имя становится вновь видимым. Объявление constant - константа - должно быть вне любых подпрограмм. Константы могут быть глобальными или локальными, но не могут быть частными. Объявления переменных внутри подпрограммы должно появиться в самом начале кода подпрограммы, перед другими исполняемыми операторами подпрограммы. Объявления верхнего уровня, вне любых подпрограмм, должны быть действительно на верхнем уровне. Они не могут находиться внутри цикла или if-оператора. Учет переменной, использованной в for-цикле, отличается от всех других. Она автоматически объявляется в начале цикла, и ее сцена заканчивается в конце цикла for. Если цикл находится внутри функции или процедуры, переменная цикла является частной переменной и не может иметь имя, совпадающее с именем любой другой частной переменной. Когда цикл находится на верхнем уровне, вне любых подпрограмм, переменная цикла является местной переменной и не может иметь имя, совпадающее с именем любой местной переменной в этом файле. Вы можете использовать одно и то же имя для переменной в различных циклах, если циклы не являются вложенными. Вам не нужно объявлять переменную цикла так, как вы это делаете со всеми другими. Диапазон величин переменной цикла определяется в операторе for и задает законные пределы ее изменения. Определение типа этой переменной является излишним и не разрешено. Итак, хотя раньше у вас уже была возможность увидеть несколько примеров, относящихся к типам переменных, пришел черед определить типы переменных более точно. Объявление переменной состоит из имени типа и списка имен объявляемых переменных. Например, object a global integer x, y, z procedure fred(sequence q, sequence r) Типы: object, sequence, atom и integer являются предопределенными. Переменные типа object могут принимать любую величину. Те же, которые объявлены с типом sequence, всегда должны быть рядами. Те из переменных, которые объявлены с типом atom, должны всегда быть атомами. Переменные типа integer должны быть атомами с целочисленной величиной от -1073741824 до +1073741823 включительно. Вы можете выполнять точные расчеты и с более значительными целочисленными величинами, до 15 десятичных знаков, но тогда нужно объявить их как atom, а не как integer.
Чтобы расширить возможности предопределенных типов, вы можете создать свои собственные типы. Все, что вы должны сделать в этом случае, так это определить функцию с единственным параметром, но объявить ее с помощью "скобок" type ... end type вместо function ... end function. Например, type hour(integer x) return x >= 0 and x <= 23 end type hour h1, h2 h1 = 10 -- все в порядке h2 = 25 -- ошибка! программа прерывается с сообщением Переменным h1 и h2 может быть присвоена только целочисленная величина в пределах от 0 до 23 включительно. После каждого присвоения h1 или h2 интерпретатор будет вызывать hour(), подавая в нее новую величину. Новая величина сначала будет проверена на "целость" (так как "integer x"). Если величина действительно целочисленная, оператор return проверит ее значение x (т.е. новое значение h1 или h2). Если hour() выдает истину, исполнение программы продолжается нормально. Если hour() выдает ложь, программа прерывается с соответствующим диагностическим сообщением. Тип "hour" может быть использован для объявления параметров подпрограмм: procedure set_time(hour h) set_time() может быть вызвана только с подходящей величиной для параметра h, в противном случае программа будет прервана с сообщением. Тип переменной будет проверяться после каждого присвоения переменной нового значения (исключая случаи, когда компилятору заранее задано, что проверка не нужна), и программа будет немедленно прервана, если функция типа выдаст ложь. Типы параметров подпрограммы проверяются каждый раз при вызове подпрограммы. Эта проверка гарантирует, что переменная никогда не сможет иметь величину, которая не соответствует типу этой переменной. В отличие от других языков, тип переменной не влияет ни на какие вычисления с этой переменной. Только величина переменной имеет значение в выражении. Тип же просто предотвращает ошибки, которые могут возникать от любой "порчи" переменной. Проверка типов может быть включена или выключена между подпрограммами с использованием специальных операторов with type_check или without type_check. По умолчанию она включена.
|