`

СПЕЦІАЛЬНІ
ПАРТНЕРИ
ПРОЕКТУ

Чи використовує ваша компанія ChatGPT в роботі?

BEST CIO

Определение наиболее профессиональных ИТ-управленцев, лидеров и экспертов в своих отраслях

Человек года

Кто внес наибольший вклад в развитие украинского ИТ-рынка.

Продукт года

Награды «Продукт года» еженедельника «Компьютерное обозрение» за наиболее выдающиеся ИТ-товары

 

Еще один «летний» язык программирования. Часть 4

0 
 

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

Еще один «летний» язык программирования. Часть 4
Двухпроцессорный полностью автономный робот Crazy Ivan www.hougaard.com/robot/) фактически полностью управляется «машиной Lua» – высокая мобильность реализации Lua позволила легко адаптировать его для исполнения процессором семейства Coldfire

Несмотря на обещанную высокую скорость «езды», от уже ставшего традицией правила знакомить читателя с не совсем новыми не совсем новостями автор отказываться не собирается. Впрочем, на этот раз для одной такой «неновости» сделано исключение – мы поговорим немного о вещах на самом деле важных, полезных и в каком-то роде очень близких к специфике нашего журнала. Речь идет о скандинавской программе формирования системы сбора информации о потреблении электроэнергии на основе имеющихся сетей сотовой связи с использованием технологии GPRS. К 2010 г. эта система будет объединять более 5 млн счетчиков электроэнергии, да и на сегодняшний день назвать ее «маленькой» язык не поворачивается – уже свыше миллиона «немобильных мобильных» терминалов автоматически «отчитываются» об израсходованной энергии. Появление такого масштабного проекта (рынок GPRS-оснащенных счетчиков электроэнергии в скандинавских странах оценивается в несколько миллиардов евро) – результат претворения в жизнь жестких правительственных программ Швеции, Финляндии и Дании по... повышению конкурентоспособности их промышленной продукции. И действительно, строгий учет потребления энергии направлен не столько на «вытряхивание» средств на пополнение бюджета («не столько» здесь ни в коем случае не означает просто отрицание), сколько на получение достоверной и актуальной информационной базы, пригодной для серьезного анализа. Ну а уже на основе результатов такого анализа можно строить не только тактические, но и стратегические планы по оптимизации энергопотребления. Результатом всего этого должно стать (и, как ни странно, становится на практике) снижение энергоемкости производства, что означает снижение себестоимости продукции. Ну а последнее – это не только повышенная конкурентоспособность, но и дополнительные средства, которые можно перераспределить, например, на проектирование новых образцов продукции. Казалось бы, все сказанное выше никакого отношения к «околокомпьютерной» тематике не имеет. Но это только на первый взгляд. GPRS-оснащенный счетчик электроэнергии на самом деле является не чем иным, как обычным встраиваемым компьютером с двумя периферийными устройствами – измерительным датчиком и GPRS-модулем. И если подобная система сравнительно проста и вполне может основываться на малоразрядном микроконтроллере, то территориально распределенная сеть из таких компьютеров несоизмеримо сложнее. В ней должны быть разрешены проблемы и синхронизации, и защищенности информации, и обеспечения ее достоверности. Впрочем, если учесть масштаб уже развернутой системы, можно утверждать, что скандинавы со всеми этими проблемами справились. И теперь конструкторы маленьких полезных в быту компьютеров направили свои усилия на решение куда менее масштабных задач – например на создание... умных вязальных спиц для забывчивых со встроенным счетчиком петель.

Как обычно, никаких выводов из этих двух «новостей» автор делать не будет. Разве что ограничится констатацией очевидного: несмотря на печальные утверждения о кризисе инженерии и безумном инновационном спаде, инженерам всегда находится применение. Лишь бы у этих инженеров профессиональные способности не нивелировались особенностями мышления (о таком клиническом, но часто встречающемся случае 53 года назад написал в своем дневнике Сальвадор Дали: «...два господина, оба инженеры по профессии и идиоты по образу мыслей»).

Скорость

Механизмы реализации типа «таблица» или ассоциативный массив в Lua очень эффективны. Это утверждение подтверждается результатами многочисленных независимых сравнительных тестов. Так, серьезный ресурс с весело-криминальным названием «Окончательная разборка между языками на платформе Win32» (dada.perl.it/shootout/index.html) демонстрирует недюжинную «скорострельность» и эффективность основного механизма Lua. Например, вычисление знаменитой рекурсивной функции Аккермана на Lua на порядок быстрее, чем на Perl и Ruby, и в пять раз – чем на Python. Скорость вычисления хэш-функций (с использованием механизма хэширования и строятся Lua-таблицы) у Lua не просто на высоте, а почти безупречна – интерпретируемые Lua-программы справляются с этой задачей менее чем в два раза медленнее написанных на языке C.

Краткий Lua-справочник

Итак, обещанная «быстрая езда» начинается с самого простого и главного даже в скриптовых языках – с синтаксиса комментариев к программам. В Lua поддерживаются две синтаксические конструкции, предназначенные для комментирования кода. Первая используется для коротких, «построчных», комментариев, вторая – для больших многострочных описаний. «Построчные» комментарии Lua начинаются парой символов и заканчиваются концом строки (такой же синтаксис у «построчных» комментариев в языках Ada, Eiffel, Haskell и Simula). В этом примере комментарий выделен курсивом:

n = ((arg and arg[1]) or 1) -- идиома ((A and B) or C) -- 
аналог (A)?B:C в языке С

Многострочный комментарий начинается последовательностью четырех символов –[[ и завершается закрывающей парой скобок ]]. Закомментированный таким образом текст может содержать квадратные скобки и пары квадратных скобок, например (как и в предыдущем примере, комментарий выделен курсивом):

--[[
Семантика конструкции вида N = A? B:C в языке С – вычисление 
выражения А и на основе полученного значения присвоение переменной 
N значения B в том случае, когда A – истинно, или С, когда 
A – ложно. [[ В Lua аналог этой конструкции выглядит так: N = 
(A and B) or C ]] ]]
]]
n = ((arg and arg[1]) or 1)

Последний, имеющий отношение к деталям комментирования исходных текстов, нюанс Lua заключается в поддержке синтаксиса знаменитой в мире скриптовых языков Unix конструкции shebang!: если первая строка любого chunk'а Lua-кода начинается с символа #, ее содержание игнорируется интерпретатором Lua:

#этой строки
-- для интерпретатора Lua
-- не существует
n = ((arg and arg[1]) or 1)

Как и в большинстве языков программирования, в Lua резервируются некоторые сочетания символов для специальных обозначений. Перечень так называемых «ключевых слов», которые нельзя использовать, например, в качестве имен переменных, здесь невелик – их всего двадцать одно:

and break do else elseif end false for function if
in local nil not or repeat return then true until while

Lua – так называемый «чувствительный к регистру» (case sensitive) язык, поэтому вы смело можете использовать по собственному пожеланию имена вида And или BReak, несмотря на то что and и break – ключевые слова языка. Впрочем, возможность что-то делать ни в коем случае не означает целесообразность это что-то делать, и прибегать к подобному приему можно в двух случаях – если вы хотите написать программу, которую сами с трудом будете понимать уже через час после ее написания, или если вас взяли в плен и заставляют программировать враги.

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

pi = 3.1415926

Объявление локальной переменной отличается только префиксом local:

local pi = 3.1415926

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

do
-- тело блока
end

Блоки в Lua часто используются именно для организации областей видимости переменных, чтобы не возникало путаницы с именами в больших Lua-программах.

Еще один «летний» язык программирования. Часть 4
Легально бесплатная система конечно-элементного анализа Finite Element Method Magnetics (femm.foster-miller.net/index.html) использует встроенный Lua-интерпретатор для расширения возможностей программы пользователем

Глобальность объявленных без спецификатора local переменных в Lua перекликается с упомянутой ранее глобальностью в этом языке типа данных «таблица». Дело в том, что все глобальные переменные Lua-программы являются на самом деле полями в... скрытых (до поры до времени или, точнее, до определенного уровня квалификации) от программиста таблицах, называемых в Lua окружением (environment tables).

Выражения в Lua, как и во многих интерпретируемых языках, являются вычислимыми, т. е. заменяемыми в программе значениями их вычислений. Три типа выражений Lua – числовые константы, строки, задаваемые значениями типа «строка» (строковые литералы), и переменные – мы уже обсудили ранее. Арифметические операторы в Lua совершенно традиционны (сложение +, вычитание -, умножение ∙, деление /, унарный минус – признак отрицательности числа, платформенно-зависимая реализация оператора возведения в степень ^), и мы о них тоже кратко уже говорили. Следует отметить, что в Lua нет излюбленных C-программистами операторов «с накоплением», например оператора сложения с накоплением, обозначаемого в языке C как +=. Операторы сравнения также вполне очевидны (<, >, <=, >=) и привычны, разве что в паре «сравнить на равенство» и «сравнить на неравенство» используется синтаксис «отрицающей тильды»: == и ~=.

Оператор присваивания (более точно было бы назвать его оператором помещения значения в контейнер) в Lua, несмотря на традиционное обозначение, скрывает за собой мощную и четко определенную функциональность. Помещение значений в контейнеры в Lua множественное, с детерминированной последовательностью действий: сначала вычисляются присваиваемые значения (справа от знака присваивания), затем, в случае несовпадения количества полученных значений и списка переменных-контейнеров (слева от знака присваивания), выполняется «выравнивание» списков значений и переменных, и наконец, значения помещаются в контейнеры-переменные попарно. Это лучше всего пояснить на примерах. Так, обмен содержимым двух переменных-контейнеров (z = 5 ; w = 7) в Lua описывается следующим выражением:

z, w = w, z

Согласно приведенному правилу интерпретатор Lua сначала формирует список значений в правой части – 7 и 5, затем проверяет количества значений и контейнеров (в данном случае они совпадают) и помещает в переменную-контейнер z значение 7, а в переменную-контейнер w – значение 5. Попробуем спровоцировать два варианта несовпадений количества переменных и значений. Например, так (по-прежнему z = 5 ; w = 7):

z, w = 100, w, z

Интерпретатор Lua в этом случае сначала сформирует список из трех значений – 100, 7, 5 и обнаружит несовпадение в числе значений с числом переменных-контейнеров. Поскольку в этом несовпадении значений больше, чем переменных, лишние значения интерпретатором просто игнорируются, и в результате переменная z будет содержать значение 100, переменная w – значение 7. Более сложный возможный вариант несовпадения демонстрирует этот пример:

z=5 ; w=7
y,z,w=w,100
Еще один «летний» язык программирования. Часть 4
Свободно распространяемый с открытыми исходными текстами редактор для программистов Yzis www.yzis.org) – гибрид знаменитого vi и мощного «моторчика» Lua-скриптинга

В данном случае интерпретатор сначала подготовит такой список значений – 7, 100. Так как переменных-контейнеров теперь больше, чем значений, интерпретатор будет записывать значения в переменные до тех пор, пока значения не «закончатся». В оставшиеся без значений-пар переменные будет автоматически записано значение nil со всеми вытекающими для этих переменных последствиями. То есть в результате выполнения этого примера в переменную-контейнер y будет записано значение 7, в переменную z – значение 100, а в переменную w... а вот переменной w уже просто не будет – сборщик мусора освободит занимаемую ею память.

Логические операторы Lua немногочисленны – их всего три, но они весьма забавны семантически. Естественно, в хорошо спроектированных языках наличие логических операторов должно предусматривать и наличие специального типа данных. Lua – язык более чем неплохо спроектированный, и такой тип данных в нем, естественно, есть – так называемый булев тип, множество значений которого включает всего два настолько важных, что для них предусмотрены два специальных ключевых слова языка: true и false. Истина и ложь – ключевые понятия логики. К слову, в Lua принято считать истиной все, что не ложно (и не nil), а не только значение true.

Над значениями булевого типа Lua, как уже говорилось ранее, позволяет выполнять всего три операции: логическое умножение («логическое и», ключевое слово Lua and), логическое сложение («логическое или», ключевое слово or) и отрицание (ключевое слово not). Семантику этих операций в Lua лучше описывать не принятыми таблицами истинности, а алгоритмически, с использованием примеров. Например, типовое выражение с бинарным оператором and выглядит так:

w and y

Если значение, записанное в w (или полученное в результате вычислений, если w – выражение), ложь (false), интерпретатор Lua даже не пытается что-то делать с y – значением всего выражения сразу считается false! С оператором or все с точностью до наоборот. Так, если в типовом выражении

w or y

значение w – истинно (т. е. true или не false и не nil), то значением всего выражения сразу становится истина, без каких-либо операций над y.

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

(w and y) or z

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

w and y or z 
-- приоритет and выше, чем or

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

w and y or z

автоматически трансформируется в

false or z

и если значение z отлично от false или nil, то все выражение сводится именно к значению z. Попробуйте «поиграться» с этой идиомой на таком примере:

w=5; y=7
print( (w>y) and 10 or 20 )

А теперь с помощью еще одной Lua-идиомы обменяйте содержимое переменных w и y и повторите вызов функции print с тем же аргументом:

w,z = z,w
print( (w>y) and 10 or 20 )

Еще одна распространенная идиома Lua использует свойство логических операторов воспринимать значение nil как аналог false:

v = v or 0

Семантика этой строки кода описывается целой процедурой, которую можно записать на псевдокоде так:

если (переменная v не существует), то
создать переменную v и записать в нее значение 0
иначе
не изменять переменную v

Базовые управляющие структуры Lua весьма традиционны – как в части синтаксиса, так и семантически. Цикл с проверкой в начале, для которого возможна ситуация, когда тело цикла не выполнится ни разу, образован конструкцией из зарезервированного слова while и уже ранее рассмотренного нами блока:

while выражение_условия do тело_блока end

В этой конструкции тело_блока будет выполняться циклически, пока выражение_условия будет истинно.

Цикл с проверкой в конце (для которого тело цикла обязательно выполнится хотя бы один раз) также не удивляет:

repeat тело_блока until выражение_условия

Конструкция for-цикла Lua более сложная. Начнем с того, что в Lua существуют два класса for-циклов – числовой и общий, отличающиеся как синтаксически, так и семантически. Числовой for-цикл в общем случае представляется следующей конструкцией:

for переменная=начальное_значение, конечное_значение, шаг 
do тело_блока end

В ней начальное_значение, конечное_значение и шаг могут быть выражениями, однако вычисляться эти выражения будут только один раз – перед «срабатыванием» всей конструкции цикла. Если шаг не указывается, то автоматически используется его значение по умолчанию – 1. Важнейшее правило числового for-цикла Lua – категорический запрет модификации в теле блока значения, записанного в переменную цикла!

Семантически числовой for-цикл Lua означает последовательное вычисление для каждого значения переменной (от начального значения до конечного, с указанным шагом) тела блока. Например, чтобы просто вывести все числа в диапазоне от 1 до 10, передайте на исполнение интерпретатору такой chunk Lua-кода:

for i=1,10 do print(i) end

А перечень нечетных чисел в этом же диапазоне можно получить так:

for i=1,10,2 do print(i) end

Принудительно завершить выполнение цикла и тем самым передать управление за его пределы можно с помощью оператора break. Работу этого оператора (и не только ее) может продемонстрировать следующий chunk:

j=0 ; for i=1,100 do if i== 51 then j=i break end end ; print(j) ;

К слову, в нем демонстрируются и области видимости Lua – попробуйте выполнить его, предварительно передав в качестве аргумента функции print() переменную i (вы уже знаете достаточно, чтобы самостоятельно объяснить, почему результатом будет nil, а не 51).

Общий for-цикл Lua – исключительно мощная конструкция для выполнения любых повторяющихся операций. Более того, о нем в Lua принято говорить, что он позволяет «обходить все, что можно построить и обойти» (термин «обход» здесь используется в том же смысле, в каком принято говорить об «обходе дерева» в программировании). Синтаксис общего for-цикла Lua следующий:

for переменная_цикла, доп_переменная in итеративное_выражение, 
do тело_блока end

Его семантику с приемлемой для ознакомительного уровня детальностью можно описать следующей словесной моделью. На каждом шаге этого цикла вызывается входящая в итеративное_выражение функция-итератор, которая возвращает некоторое значение или nil. Если возвращенное значение – nil, то выполнение цикла прекращается, в противном случае значение записывается в переменную цикла, выполняется тело блока, и весь процесс повторяется. Lua располагает развитым набором уже готовых функций-итераторов, и, естественно, программист может создавать собственные итераторы для решения своих задач – итераторы являются обычными функциями Lua.

На for-циклах экзотика Lua в части управляющих структур заканчивается – условный оператор в языке можно назвать совершенно обыденной конструкцией:

if выражение_условия then тело_блока end

Возможны также вариации условного оператора с использованием ключевых слов elseif и else.

Продолжение следует.

Ready, set, buy! Посібник для початківців - як придбати Copilot для Microsoft 365

0 
 

Напечатать Отправить другу

Читайте также

 

Ukraine

 

  •  Home  •  Ринок  •  IТ-директор  •  CloudComputing  •  Hard  •  Soft  •  Мережі  •  Безпека  •  Наука  •  IoT