Упражения в программировании: расчеты на языке Lua
08.09.08Мой Компьютер, №12, 17.03.2008
Предыстория
Так случилось, что в процессе обучения в университете я столкнулся с программой FEMM. Что это за программа, в данном случае не важно, нам она интересна другим: в ней есть встроенный интерпретатор языка программирования Lua (в МК уже были статьи на тему самого языка). Этот интерпретатор значительно расширяет функциональность программы. Если говорить конкретно в контексте FEMM, то использование данного ПО без встроенного Lua-интерпретатора вообще крайне затруднительно.
Что он дает? ПО имеет API, который мы можем использовать в Lua-скриптах, соответственно, комбинация «Lua-скрипты + API-функции» может обеспечить значительно более простое выполнение рутинных операций. И вот у меня возникла мысль: как же это реализуется? Пока мы не пошли дальше, отмечу также, что Lua активно применяется при разработке игр. Те, кто поинтересуются количеством игровых проектов, в которых используется рассматриваемый язык, думаю, будут удивлены.
Предложение
Итак, представим ситуацию: у нас есть приложение для нахождения корней уравнения. Уравнение прописывается в коде программы, пользователь же задает границы интервала поиска корней и нажимает кнопку «Решить», после чего результаты выводятся на экран. Понимаю, возможностей, мягко говоря, не густо. Но это сейчас и не принципиально. Рассмотрим два интересных момента, которых можно добиться, используя Lua в нашем приложении. Первый: что, если у пользователя есть не один, а целый набор интервалов, на которых ему нужно проверить наличие корней? Тогда он вынужден по очереди вводить интервалы, жать на кнопку «Решить». А если несколько упростить этот процесс? Например, написать Lua-скрипт, в котором с помощью цикла, меняя интервал поиска, нужное количество раз вызывать функцию решения уравнения, а нашу программу попросить этот скрипт выполнить. Второй: если мы захотим, чтобы наша чудо-программа решала другое уравнение, нам необходимо сначала править код, где задается уравнение, потом перекомпилировать программу.
Что, если мы не хотим каждый раз выполнять эти процедуры? В таком случае предлагается написать функцию определения уравнения на Lua и вынести в отдельный файл. И потом в нашей программе при вызове данной функции будем выполнять соответствующий Lua-код.
Вы можете спросить: какой резон вообще в реализации такой возможности? Попробую ответить: а что, если у нас реально большая программа, для которой компиляция занимает существенное время? Мы меняем всего ничего — и что, каждый раз ждать? Или, к примеру, мы еще сами не определились с исследуемой функцией и экспериментируем?
Отвлекся… Теперь самое главное: реализация в коде.
Ресурсы
Язык программирования для основной (назовем ее так) программы — С++. Среду разработки я возьму Borland C++ Builder 6.0. С сайта www.lua.org возьмем необходимые исходники, версия языка 5.1.3 (именно этой, самой свежей, логически код для других версий отличатся не будет, а вот синтаксически — немного), а также обязательно мануальчик, главный источник информации для людей, имеющих дело с Lua.
Код
Начнем по порядку. Для начала приведу содержимое Lua-скрипта, который автоматизирует подстановку интервалов с последующим решением уравнения.
—script1.lua
leftBounds = {-20, -15, -10, -5, 0}
rightBounds = {20, 15, 10, 5, 2}
i = 1
n = 5
repeat
setB(leftBounds[i],rightBounds[i])
Solve()
i = i + 1
until i > n
Поясняю содержимое построчно: комментарий с именем файла; массив левых границ; массив правых границ; счетчик; количество элементов в массиве; цикл, в котором осуществляется установка границ и вызов функции решения уравнения.
Теперь приведу код основной программы. Сразу весь, пояснять буду по ходу. Единственное, что опущу, — код функции уравнения, о нем чуть позже, и код функции решения уравнения — я брал самую простую дихотомию, вам же предоставляю свободу выбора, тем более, что мы же не алгоритмы поиска корней обсуждаем.
#include <iostream.h>
extern «C»{
#include «lua.h»
#include «lauxlib.h»
#include «lualib.h»
#include «luaconf.h»
}
double lB, rB; // границы интервала
//———
int setBounds(lua_State* myLua){
lB = lua_tonumber(myLua, -2);
rB = lua_tonumber(myLua, -1);
return 1;
}
//———
int solve(lua_State* myLua){
// тут код решения уравнения
return 1;
}
int main(int argc, char* argv[])
{
lua_State* myLua = lua_open();
if (NULL == myLua){
cout << «Error!»;
return 0;
}
luaL_openlibs(myLua);
lua_register(myLua,»setB»,setBounds);
lua_register(myLua,»Solve»,solve);
luaL_dofile(myLua, «script1.lua»);
lua_close( myLua );
return 0;
}
Подключая заголовочные файлы, не забываем про extern «C»{}. Lua написан на С. Далее рассмотрим содержимое функции main(). Для начала нам нужно запустить экземпляр виртуальной машины Lua (я понял из мануала именно так), что мы и делаем в строке lua_State* myLua = lua_open();
Далее, я думаю, и без моего комментария понятно, что осуществляется проверка, все ли у нас хорошо. Стандартные функции Lua доступны из соответствующих библиотек, которые мы и подключаем с помощью luaL_openlibs(myLua);, подключим сразу все стандартные, хотя можно и по отдельности.
Теперь вспомним, что интерфейс в виде функций setB и Solve, который мы предоставили Lua для использования в скриптах, надо же как-то связать с С++, то есть с функциями, его реализующими (setBounds и solve соответственно). Это выполняется на участке кода lua_register(myLua,»setB»,setBounds); lua_register(myLua,»Solve»,solve);.
Далее выполним скрипт с помощью luaL_dofile(myLua, «script1.lua»); и закончим работу с нашей myLua.
Также обратите внимание на тип и параметр функций setBounds и solve, такая ситуация обязательна.
И последнее: все общение С++ — Lua и обратно происходит через специальный стек, нумерация в нем начинается с 1, номер последнего элемента -1; вызывая в Lua-коде функцию setB с двумя параметрами, мы помещаем в стек эти самые два параметра, поэтому в функции setBounds мы и пишем lB = lua_tonumber(myLua, -2);, то есть левой границе присваиваем второе с конца значение в стеке, преобразовывая его к числу.
Поздравляю! Наша программа поддерживает Lua-скрипты.
Теперь поговорим о том, как же не прописывать уравнение в теле программы.
Пускай мы решаем уравнение x*x — 2 = 0. Создадим файл fun.lua, а в нем напишем следующее:
function func(a)
return a*a — 2
end
Пояснения по коду, думаю, не нужны. С++ функция уравнения примет вид:
double f(double x){
lua_State* locLua = lua_open();
if (NULL == locLua){
cout << «Error!»;
return 0;
}
luaL_openlibs(locLua);
luaL_dofile(locLua, «fun.lua»);
lua_getglobal(locLua, «func»); // функция, которая будет вызвана
lua_pushnumber(locLua, x); // параметр
lua_call(locLua, 1, 1); // вызываем функцию с одним параметром и возвращаем один результат
double res = lua_tonumber(locLua, -1);
lua_close( locLua );
return res;
}
Пояснения по коду я дам в тех моментах, где он отличается от обговоренного выше, начиная со строк с комментариями в коде. Сначала мы находим функцию, которая будет вызвана, потом помещаем в стек значение параметра для этой функции и наконец вызываем. Результат выполнения функции заносится в последний элемент стека, считывая значение которого, мы и получаем возможность предоставить его основной программе.
Еще раз поздравляю! Вторая цель достигнута. Теперь нам не требуется перекомпилировать программу, каждый раз, когда мы изменяем нашу функцию.
Заключение
Может показаться, что я упустил массу важных вещей, нюансов… но ведь ставился вопрос «как?». И на него ответ получен. Для заинтересовавшихся — в сети есть огромное количество информации, специализированные ресурсы и т.д. Спасибо за внимание! Удачи!
P.S. Позволю себе отметить одно из главных достоинств приведенного учебного примера: он РАБОТАЕТ!!! 🙂
Владимир ДУБИНИН
Web-droid редактор
Не пропустите интересное!
Підписывайтесь на наши каналы и читайте анонсы хай-тек новостей, тестов и обзоров в удобном формате!
![Read more](https://hi-tech.ua/wp-content/themes/softpress/img/new-arrow-one.png)
![Articles](https://hi-tech.ua/wp-content/themes/softpress/img/arrow.png)
Обзор клавиатуры Logitech G PRO X TKL RAPID: тонкие настройки
![views](https://hi-tech.ua/wp-content/themes/softpress/img/icons/eyeglasses.png)
![comments](https://hi-tech.ua/wp-content/themes/softpress/img/icons/speach.png)
![Logitech G PRO X TKL RAPID](https://hi-tech.ua/wp-content/uploads/2025/02/logitech-g-pro-x-tkl-rapid-21-300x225.jpg)
Новая клавиатура Logitech G PRO X TKL RAPID не просто предлагает фирменно высокое качество и механические переключатели. Она также позволяет менять некоторые параметры срабатывания кнопок. Расскажем подробнее
![News](https://hi-tech.ua/wp-content/themes/softpress/img/arrow.png)
Новый процессор MediaTek Dimensity 6400 поддерживает 5G, камеры 108 Мпикс и экраны 120 Гц MediaTek процессор
MediaTek представила новый процессор Dimensity 6400, который стал обновленной версией прошлогоднего Dimensity 6300.
Смартфон ZTE Blade V70 Max с экраном 6,9 дюймов имеет аккумулятор 6000 мАч ZTE Андроид смартфон
ZTE представила новый смартфон Blade V70 Max, который появился на официальном сайте компании. Модель дополнила линейку Blade V70 и Blade V70 Design, представленных в ноябре прошлого года.