Язык Lua и Corona SDK (1-3 часть)

в 18:19, , рубрики: corona sdk, Lua, статьи для начинающих

image

Если вы решили стать разработчиком игры и ваш выбор был сделан в игрового движка Corona SDK, возможно у вас есть проблема в понимании языка Lua — эта статья станет хорошей отправной точкой. По своему этот язык прекрасен и во многих отношениях необычен. Я постарался собрать в одну статью все наиболее необходимые сведения но их оказалось больше чем можно размещать в одной публикации и мне пришлось разделить статью на 3 части, эта первая часть и в ней мы рассмотрим следующие вопросы:

  • Порядок комментирования исходников
  • Переменные, константы, область видимости
  • Модули и организация проекта
  • Условные операторы

Порядок комментирования исходников

Почти в любом языке программирования имеется понятие комментариев, Lua не исключение. Что вообще такое комментарий — это пояснения к исходному тексту программы, находящиеся непосредственно внутри комментируемого кода. Синтаксис комментариев определяется языком программирования. С точки зрения компилятора или интерпретатора, комментарии — часть текста программы, не влияющая на её семантику. Т.о. комментарии ни как не влияют на порядок выполнения кода и вы их пишите для себя и тех кто через какое-то время может начать изучать ваш исходник. Комментарии могут использоваться в качестве инструмента отладки, т.е. вы можете разместить в комментарий кусок кода временно его исключив из выполнения и тем самым упростив момент поиска ошибки в другом участке. В Lua есть 2 вида комментариев.

Однострочный комментарий

Все написанное от последовательности символов "--" до конца строки считается комментарием.

Многострочный комментарий

Многострочный комментарий начинается последовательностью "--[[" и заканчивается через любое количество строк последовательностью "]]" Далее примеры применения комментариев:

--[[Функция принимает 2 параметра a и b
возвращает сумму параметров]]
function summ(a,b)
	return a + b
end

print(summ(5,7))--результат 12

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

  • Хороший код сам себя комментирует — т.е. если вы пишите очевидно и называете переменные обдуманно в большинстве случаев комментарии не требуются
  • Не пишите комментарии — для комментария. Т.е. не стоит писать комментарии там где и так все ясно.
  • Краткость — сестра таланта. Делайте комментарии простыми и понятными.
  • Комментируйте назначение файла и функций.

Переменные, константы, область видимости

Большинство языков программирования имеют переменные. Под этим понятием в большинстве случаев понимается — поименованная, либо адресуемая иным способом область памяти, адрес или имя которой можно использовать для осуществления доступа к данным и изменять значение в ходе выполнения программы. В Lua переменные могут создавать прямо там где их нужно применять, т.е. нет определенного блока (как в языках типа Pascal) где необходимо заранее объявлять переменные перед тем как их использовать. Приведу пример объявления переменных:

a = 1
b = 3
local c = 'example'
d,pi = 2, 3.14
a = 'stroka'

Знатоки других языков думаю заметили несколько особенностей:

  • тип переменных не нужно указывать, значение определяет её тип
  • тип переменных можно менять, т.е. если переменная была численной (в примере «a»), а далее ей присвоили строчное значение она стала строчной
  • доступно параллельное присвоение через один знак "=" (переменные d,pi). Это свойство может значительно укоротить момент стартовой инициализации, так же позволяет выполнять обмен значений переменных без промежуточной переменной например так a,b = b,a
  • ключевое слово local. Это слово используется для того что бы ограничить область видимости переменных, если переменная объявлена локальной внутри операционного блока (функция, цикл, условный оператор) она будет видна только внутри него. Если де локальная переменная объявлена в глобальном контексте файла она будет видна с этой строки и до конца файла (подробней об области видимости вы узнаете в немного ниже)

Остановимся на константах. Под константой понимается — переменная значение которой не меняется в процессе использования. Часты бывает удобно некоторые значения многократно применяемы в проекте (например версия приложения) хранить в константах, в некоторых языках имеется инструмент создания констант в прямом смысле этого слова, т.е. переменных которые были единожды объявлены и в дальнейшем их значение невозможно изменить, в Lua такой инструмент отсутствует т.е. пенять можно все, (даже реализацию функции) и по этой причине удобным вариантом для защиты от смены констант можно считать — порядок их именования. Лучше всего константы именовать каким-то особым образом что бы в коде они были более заметными, например БОЛЬШИМИ_БУКВАМИ_СО_СЛОВАМИ_ЧЕРЕЗ_ПОДЧЕРКИВАНИЕ, но это не правило а скорее совет.

Кратно опишу формат допустимых имён переменных, констант и функций, некоторые вещи декларативны и их нельзя нарушать, другие просто советы:

  • переменные могут содержать латинские прописные и строчные буквы, цифры и символ подчеркивания "_"
  • переменные не могут начинаться с цифры
  • переменные не могут иметь имена зарезервированных слов: and, break, do, else, elseif, end, false, for, function, if, in, local, nil, not, or, repeat, return, then, true, until, while
  • зависимость от регистра. Как и в большинстве языков в Lua регистр является определяющим факторов в уникальности переменной, т.е. lua, LUA, lUa и LuA это все разные переменные.
  • не стоит злоупотреблять названием переменных начинающихся с символа нижнего подчеркивания, если конечно вы уверены что эти имена точно не используются языком для именования внутренних механизмов
  • используйте обдуманные имена переменных которые имеют широкую область видимости. Это стоит понимать так — если переменная используется в большой части проекта она не должна иметь имя ни чего не означающее и короткое
  • разделяете формат именования переменных, констант и функций. Например для констант уможно применять ТАКОЙ_ФОРМАТ, для переменный примерно_такой, а для функция использоватьТакойФормат — это сделает код более удобным.

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

  • переменную не видно — вам кажется что все хорошо, а переменная равна nil (не определена). Эту ошибку как правило легко выявить но не всегда легко устранить.
  • переменную видно там где она не используется — эта на первый взгляд мелкая проблема может привести к крайне сложным багам, например вы не ожидаете что переменная из другого участка кода будет видна в этой области и делаете поверку на ее несуществование (что бы ее инициализировать) и проверка не заканчивается успехом и ваш инициализирующий код не выполняется что приводит к использованию значения которое было определено в другом месте. Что бы избежать этой проблемы всегда и все переменные старайтесь делать минимально видимыми используя слово local. Использование глобальных переменных всегда нужно стараться сводить к минимуму

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

Модули и организация проекта

Программный модуль — функционально законченный фрагмент программы, оформленный в виде отдельного файла с исходным кодом. Как вы уже знаете выполнение любого проекта в Corona SDK начинается с файла main.lua. Если писать всё приложение в одном файле у вас может возникнуть масса трудностей, приведу пример:

  • Большой объем кода. исходный текст почти любой полноценной игры может включать сотни, тысячи, десятки тысяч, а в некоторых случаях и более ста тысяч строк кода, если разместить весь код в одном файле на некотором этапе сопровождать этот проект станет просто невероятно сложно
  • Множество сцен. в большинстве случаев реальные приложения содержат более одной сцены (функционального экран), например различные меню, экраны загрузки, возможно несколько боевых сцен, сцены открытого мира и т.д. опять же если разместить все в один файл придется при переходе между сценами нагромождать код десятками процедур создания и удаления сцены, т.е. код опять же будет сильно усложняться
  • Универсальный переносимый код. В процессе создания своих прилодений вы часто будете сталкиваться с однотипными задачами, которые вы будете переносить в библиотеки, а далее эти библиотеки вы сможете легко переносить в ваши следующие проекты, если же писать все в одном файле вопрос о переносимости готовых библиотек будет весма затруднителен, так как полезный код придется вычленять фрагментами из огромного файла.

Возможны и другие проблемы если неверно организовать проект и писать все в один файл main.lua, но в наших уроках мы так делать не будем и далее я покажу тот способ который я выбрал для своих проектов и который не раз себя хорошо зарекомендовал, принимать его или придумывать свой — решать вам, но дальнейшие примеры проектов я буду оформлять таким способом. Таким образом мы пришли к заключению, что каким-то образом нам нужно разделать исходник проекта на несколько файлов, более того эти файлы будут иметь различное назначение и исходя из этого у каждого типа файлов будет свой порядок именования определяемые префиксом в имени. Этот способ лично мой и я не утверждаю что он лучший, но так как дальнейшие примеры игр которые вы увидите в моих статьях будут иметь такой механизм оформления проекта, я решил взять его за основу. Кратко пробежимся по типам файлов которые могут пригодится.

Сцена

Сцена — префикс sc. Формат имени файлов scName.lua. Где имя начинается с заглавной буквы и определяет назначение файла. Для остальных типов это правило так же будет действовать поэтому я буду указывать только их префиксы. Файл сцены имеет довольно строгий формат и он определяется требованиями библиотеки Corona SDK — composer. Рассмотрению этой библиотеки будет посвящен отдельный урок пока что прикладываю шаблон исходного файла:

local composer = require( "composer" ) 
local scene = composer.newScene()
 
-- -----------------------------------------------------------------------------------
-- Код за пределами функций сцены вополняется один
-- не удяляется в рамках "composer.removeScene()"
-- -----------------------------------------------------------------------------------
 
-- -----------------------------------------------------------------------------------
-- обработчик сцены
-- -----------------------------------------------------------------------------------
 
-- Создание сцены
function scene:create( event )
	local sceneGroup = self.view
	-- Выполняется при создании сцены, до ее загрузки
end
 
 
-- Отображение сцены
function scene:show( event )
	local sceneGroup = self.view
	local phase = event.phase
	if ( phase == "will" ) then
		--Сцена выключена но скоро появится на экран
	elseif ( phase == "did" ) then
		-- Сцена полностью загружена на экран
	end
end
 
 
-- Скрытие сцены
function scene:hide( event )
	local sceneGroup = self.view
	local phase = event.phase
	if ( phase == "will" ) then
		-- выполняется перед скрытием сцены
	elseif ( phase == "did" ) then
		-- выполняется после скрытия сцены
	end
end
 
 
-- уничтожение сцены()
function scene:destroy( event )
	local sceneGroup = self.view
	-- код выполняется до удаления сцены
end 
 
-- -----------------------------------------------------------------------------------
-- Создание обработчиком сцены
-- -----------------------------------------------------------------------------------
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )
-- -----------------------------------------------------------------------------------
 
return scene

Стоит отметить что файлы сцен не нужно каким-то образом подключать к проекту, так как их подключение будет производиться в рамках использования библиотеки composer.

Универсальная библиотека

Универсальная библиотека — префикс lib. В эти файлы нужно размещать готовые универсальные функции которые в дальнейшем можно будет применять в различных частях приложения, например тригонометрические функции ваших проектов можно разместить в файле libTriginom.lua а дальше эти функции будут пополняться и в следующих проектах их можно будет легко использовать просто переносом файла и подключением. Старайтесь избегать в универсальных библиотеках привязки к элементам графического дизайна приложения, а исходные значения передавайте параметрами вызова. Приведу пример простой библиотеки:

---------------------
--  ТРИГОНОМЕТРИЯ  --
-- libTrigonom.lua --
---------------------
local M = {}

--длина гипотенузы при катетах a,b
M.hypotenuse = function(a,b)
	--c = √(a²+b²)
	local c = math.sqrt(a*a + b*b)
	return c
end

--длина отрезка
M.line_len = function(x1,y1,x2,y2)
	--length = √((X2-X1)²+(Y2-Y1)²)
	local length = math.sqrt((x2 - x1)^2 + (y2 - y1)^2)
	return length
end

--угол между двумя точками
M. get_angle = function(x1,y1,x2,y2)
	return ((math.atan2(y1 - y2, x1 - x2) / (math.pi / 180)) + 270) % 360
end

return M

Эту библиотеку можно подключить поместив в main.lua следующий код:

Trigonom = require("libTrigonom")

Приведу пример использования функций в любой части проекта:

local a,b = 3,4--катеты
local c = Trigonom.hypotenuse(a,b) -- получаем гипотенузу
print(c)-->> 5

local x1,y1 = 34,-56	--точка A
local x2,y2 = -134,56	--точка B
local length = Trigonom.line_len(x1,y1,x2,y2) -- длина отрезка
print(length)-->> 201.91087142598

local angle = Trigonom.get_angle(x1,y1,x2,y2)
print(angle)-->> 236.30993247402

Виджет/сервис

Виджет/сервис — префикс svc. Виджет — аналог библиотеки, но основной функционал основан на создании графических компонентов. С помощью виджетов удобно создавать составной интерфейс, т.е. имеется основные статические компоненты сцены которые создаются в рамках основного кода в файлах sc и поверх статических компонентов могут создавать динамические виджеты в виде всплывающих окон, перемещаемых форм и прочих удобств. Удобней всего все виджеты проекта оформлять с единым интерфейсом(стандартным набором функций), например таким:

  • show() — создает окно с функционалом виджета и помещает его в заданном месте экрана
  • refresh() — обновляет состояние компонентов виджета делая его актуальным текущему моменту. Например если этот виджет является часами его необходимо обновлять раз в секунду или минуту (если нет секундной стрелки).
  • hide() — скрывает или удаляет весь интерфейс виджета

Стоит заметить что нет ни какого риска в том что у вас в различных виджетах одинаковые названия методов (функций) — show/refresh/hide для доступа к функции необходимо добавление имени виджета (как и в случае с библиотеками) и по этой причине глобально пространство имен не будет засорено.

Приведу пример виджета (часы):

-------------------------
-- Виджет создает часы --
-------------------------
M = {
	GR = {}, --массив групп
	GUI = {},--массив хрянящий интерфейс
}

--получение текущей временой зоны
M.get_timezone = function()
  return os.difftime(os.time(), os.time(os.date("!*t", os.time()))) / 3600
end

--обновление текущего состояния компонентов выбранного id
M.refresh = function(id)
	local cur = M.GUI[id].capt
	local tm = os.date("!*t", os.time() + 3600*cur.GTM)
	--функция добавлет спереди 0 к числам меньше 10
	local norn_dig = function(n)
		return n < 10 and '0'..n or n
	end
	local sec = cur.is_sec and ":"..norn_dig(tm.sec) or ''
	cur.text =  norn_dig(tm.hour)..':' .. norn_dig(tm.min) .. sec
end

--Создание часов, возврат id созданных часов
--Параметры(все необязательные):
--	x - позиция центра по x (пр умолчанию центр экрана по x)
--	y - позиция центра по y (пр умолчанию центр экрана по y)
--	is_sec - отображать секунды (по умолчания да - true)
--	GTM - временой пояс, по умолчанию текущий
M.show = function(p)
	--создаем значения п умолчанию
	p = p and p or {}
	p.x = p.x and p.x or _W * .5
	p.y = p.y and p.y or _H * .5
	if p.is_sec == nil then
		p.is_sec = true
	end
	if p.GTM == nil then
		p.GTM = M.get_timezone()
	end
	--созадем часы
	local id = #M.GR + 1
	M.GR[id] = display.newGroup()--группа экземпляра часов
	M.GUI[id] = {}--место храние компонентов
	--подкладка - скругленный прямоугольник
	M.GUI[id].bg = display.newRoundedRect(M.GR[id], p.x, p.y, _W * .33, _H * .08, _H * .02)
	M.GUI[id].bg.strokeWidth = _H * .005--ширина окаймления
	M.GUI[id].bg:setStrokeColor(0,1,.5)--цвет окаймления
	M.GUI[id].bg:setFillColor(1,.5,0)--цвет основания
	--при клике по часам они уничтожаются
	M.GUI[id].bg:addEventListener('touch',function(event)
		if event.phase == 'began' then
			M.hide(id)
		end
	end)
	--текст часов
	M.GUI[id].capt = display.newText(M.GR[id], '', p.x, p.y, nil, _H * .05)
	M.GUI[id].capt:setFillColor(0)
	M.GUI[id].capt.GTM = p.GTM
	M.GUI[id].capt.is_sec = p.is_sec
	M.refresh(id)
	--таймер обновления экземпляра часов
	timer.performWithDelay( 500, function(event)
		if not M.GUI[id].capt.text then
			timer.cancel( event.source )
		else
			M.refresh(id)
		end
	end, 0 )

end

--удаление часов с переданным id или всех если id не передан
M.hide = function(id)
	if id then
		display.remove(M.GR[id])
	else
		for i = 1,#M.GR do
			display.remove(M.GR[i])
		end
	end
end

return M

порядок подключения и применения сервиса следующий:

_W = display.contentWidth
_H = display.contentHeight
Clock = require "svcClock"
Clock.show()

сервисы как и библиотеки стоит делать достаточно универсальными так как они то же кандидаты на повторное использование, большую часть параметров (в идеальном случае все) нужно делать не обязательными, т.е. имеющими значения по умолчанию.

Базы данных

Базы данных — префикс db. Эти файлы не содержат функций но хранят сведения о статических настройках объектов системы, например характеристики юнитов. Приведу пример базы данных с настройками юнитов:

return {
	peasant = {
		title = {ru = 'Крестьянин', en = 'Peasant'},
		force = 1,
		defence = 2,
		speed = 1,
	},
	spearman = {
		title = {ru = 'Копейщик', en = 'Spearman'},
		force = 3,
		defence = 2,
		speed = 2,
	},
	archer = {
		title = {ru = 'Лучник', en = 'Archer'},
		force = 5,
		defence = 1,
		speed = 3,
	},
	knight = {
		title = {ru = 'Рыцарь', en = 'Knight'},
		force = 7,
		defence = 3,
		speed = 4,
	},
}

стоит отметить что подобный тип реализации для более крупных баз бывает проще заменить на такую реализацию

local M = {
	name = {'peasant','spearman','archer','knight'},
	title = {
		{ru = 'Крестьянин', en = 'Peasant'},
		{ru = 'Копейщик', en = 'Spearman'},
		{ru = 'Лучник', en = 'Archer'},
		{ru = 'Рыцарь', en = 'Knight'},
	},
	stats = {
		{force = 1, defence = 2, speed = 1,},
		{force = 3, defence = 2, speed = 2,},
		{force = 5, defence = 1, speed = 3,},
		{force = 7, defence = 3, speed = 4,},
	},
	M.res = {},
}

for i = 1,#M.name do
	M.res[M.name[i]] = {
		title = M.title[i]
		force = M.stats[i].force,
		defence = M.stats[i].defence,
		speed = M.stats[i].speed,
	}
end

return M.res

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

units = require('dbUnits')
print(units.archer.title.ru)--печатает русский заголовок одного из юнитов

Вставка кода

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

--main.lua
local P = '123'
k = 23.2

local function sum(a,b)
	return a+b
end

local function razn(a,b)
	return a-b
end

local a,b = 2,3
print(sum(a,b))
print(razn(b,a))

теперь вынесем функции sum и razn в отдельный файл InsExample.lua имеем 2 файла:

-- InsExample.lua
function sum(a,b)
	return a+b
end

function razn(a,b)
	return a-b
end

--main.lua
local P = '123'
k = 23.2

require 'insExample'

local a,b = 2,3
print(sum(a,b))
print(razn(b,a))

Стоит обратить внимание, что при переносе в отдельный файл я сделал функции глобальными (убрал local), в противном случае они выйдут из области видимости main.lua. Так же нужно понимать что внутри файл insExample.lua не будет видна локальная переменная Р, но будет видна глобальная переменная k, а если объявить переменную k после require то и она не будет видна.

ВАЖНО: При любом варианте использования require для любых манипуляций с файлами этот вызов выполняется только один раз при первом проходе нити выполнения через этот участок кода, это нужно всегда учитывать если не хотите долго мучиться со сложными багами. Так же если вы будете как и я в модулях типа db/lib/svc использовать некую переменную M для сборки данных перед return (или любую другую) не забывайте делать эту переменную локальной, так как в противном случае каждый следующий require будет переписывать результат предыдущего и это будет приводить к еще более сложной ошибке.

Условные операторы

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

Для сравнения данных используются операции сравнения. Каждая такая операция состоит из двух переменных, между которым стоит оператор сравнения, всего их в Lua имеется 6:

  • == равно
  • ~= не равно
  • < меньше
  • > больше
  • <= меньше или равно
  • >= больше или равно

В сущности результатом любой операции сравнения является false или true (ложь или правда), эти значения характерны для типа ,boolean который наряду с другими типами будет рассмотрен ниже. Теперь рассмотрим с помощью каких конструкций языка применяются операции сравнения.

Оператор IF

Сразу к примеру — если a больше b, присваиваем cзначение true

if a > b then
	c = true
end

Как видите порядок применения довольно простой. Но как обычно есть тонкости — содержимое блока if является операционным блоком т.е. областью ограничения локальной видимости, т.о. если переменную c сделать локальной она не будет видна после end, естественно выход есть если нам нужна локальная переменная c видна за блоком If ее нужно объявить перед if:

local c = false
f a > b then
	c = true
end

Как я говорил выше имеется тернарный способ организации ветвления и описанный выше код можно легко записать этим способом

local c = a > b

Вот так просто:) Теперь пару слов о том как сделать более сложное условие в if. Имеются логические операторы and(и) и or(или). Они работает примерно так же как и в большинстве языков, хотя есть и свои особенности.

AND

Условия совмещенные операторами and должны выполниться все, условия выполняются слева направо если в процессе выполнения проверок не выполнилось одно условие, следующие проверки не будут проводиться. Это весьма важный момент и его нужно четко понимать, так как это можно использовать как в своих целях так и иметь из-за этой особенности массу сложных багов, приведу пример полезного использования. Имеется объект находящийся скажем на третей глубине вложения в другие объекты ну скажем такой obj1.obj2.obj3 мы хотим сделать проверку на его существование, и на первый взгяд можно сделать так:

if obj1.obj2.obj3 then
	c = true
end

вам может показаться что это хорошая идея, но на самом деле это не так, допустим не существует объект obj1 или obj2 и в этом случае возникнет ошибка т.е. проверку нужно делать так:

if obj1 and obj1.obj2 and obj1.obj2.obj3 then
	с = true
end

Теперь проверка будет выполнения безопасно и без ошибок. Достаточно похожий механизм и при реализации тернарным оператором

local c = obj1 and obj1.obj2 and obj1.obj2.obj3 and true

OR

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

if a>b or c>b or k<0 then
	c = true
end

полностью аналогична тернарная реализация:

local c =  a>b or c>b or k<0

NOT

Логическая операция отрицания. Условие перед которым стоит not понимается наоборот, т.е. если a > b это понимается как a больше b, то not a > b понимается как a НЕ больше b.

IF ELSE

Часто возникает необходимость в следующем виде ветвления: если выполнилось условие делаем действие, в противном случае другое действие. Это реализуется оператором if else и реализация выглядит следующим образом:

if a>b then
	c= true
else
	c =false
end

Как видите ни чего сложного. Тернарная реализация этого примера будет иметь как оператор AND так и оператор OR:

local c = a>c and true or false

IF ELSEIF ELSE

Имеется еще один вариант условия: если выполнено условие1 делаем — действе1, если условие2 делаем — действеи2… если условиеN делаем — действиеN, в противном случае действиеX. Во многих языках этот тип ветвления реализован средствами специальных операторов множественного ветвления вроде switch, в Lua этого нет есть свой способ:

if a>b or d<b then
	k  = 1
elseif b>c  and a<b then
	k = 2
elseif c>d then
	k = 3
else
	k = 0
end

Как видите — все просто. Есть особенности в порядке выполнения составного оператора IF ELSEIF ELSE: во-первых else — не обязателен, во-вторых если произошло первое вхождение в условие, то дальнейшие проверки не выполняются, а происходит переход на строчку следующую за закрывающим end. Может показаться что досрочный выход из составного оператора не имеет значения и это не может вызвать ошибок, но это не так, приведу пример который показывает что неверное понимание этого принципа может дать неожидаемый результат:

k = 1
if k > 0 then
	k = 2
	print(k)
elseif k > 1 then
	k = 3
	print(k)
end

Результат выполнения этого действия будет только один print -> 2. В заключении покажу как реализовать тернарным оператором первый пример использования IF ELSEIF ELSE:

local k = (a>b or d<b) and 1 or (b>c  and a<b) and 2 or c>d and 3 or 0

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

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

Автор: exe_com

Источник

Поделиться

* - обязательные к заполнению поля