<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://wiki2.linuxformat.ru/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>LXF123:Lua - История изменений</title>
		<link>http://wiki2.linuxformat.ru/index.php?title=LXF123:Lua&amp;action=history</link>
		<description>История изменений этой страницы в вики</description>
		<language>ru</language>
		<generator>MediaWiki 1.11.1</generator>
		<lastBuildDate>Wed, 13 May 2026 21:01:33 GMT</lastBuildDate>
		<item>
			<title>Crazy Rebel: /* Функции ''Lua'' */</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF123:Lua&amp;diff=10778&amp;oldid=prev</link>
			<description>&lt;p&gt;&lt;span class=&quot;autocomment&quot;&gt;Функции ''Lua''&lt;/span&gt;&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;← Предыдущая&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Версия 05:57, 20 сентября 2010&lt;/td&gt;
			&lt;/tr&gt;
		&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Строка 22:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Строка 22:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt; print(foo)&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt; print(foo)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;-&lt;/td&gt;&lt;td style=&quot;background: #ffa; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;Первые четыре строки в пояснениях особо не нуждаются. Ключевое слово '''function''' объявляет функцию. Далее следуют ее имя и список аргументов, заключенный в скобки (у нас он пуст). Тело функции – это блок, обязанный заканчиваться ключевым словом '''end'''. Обратите внимание, что хотя наша функция возвращает значение 1, оператором '''return''', тип возвращаемого значения в ее объявлении не указывается. Аналогично тому, как одна и та же переменная ''Lua'' может принимать значения любых определенных в языке типов, одна и та же функция ''Lua'' может возвращать значения всех возможных типов. Все-таки ''Lua'' не зря назван именем небесного тела, обозначающего в символике многих народов переменчивость и обманчивость. В пятой строке мы распечатываем значение, возвращаемое '''foo()'' (при этом, естественно, выполняется сама функция '''foo()'''). Шестая строка выглядит интереснее. В нашем фрагменте, '''foo''' – это переменная, содержащая значение типа «функция» (на самом деле – идентификатор функции, но об этом ниже). В шестой строке мы печатаем значение переменной '''foo''', а не результат, возвращаемый функцией. Вот что мы получим:&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;Первые четыре строки в пояснениях особо не нуждаются. Ключевое слово '''function''' объявляет функцию. Далее следуют ее имя и список аргументов, заключенный в скобки (у нас он пуст). Тело функции – это блок, обязанный заканчиваться ключевым словом '''end'''. Обратите внимание, что хотя наша функция возвращает значение 1, оператором '''return''', тип возвращаемого значения в ее объявлении не указывается. Аналогично тому, как одна и та же переменная ''Lua'' может принимать значения любых определенных в языке типов, одна и та же функция ''Lua'' может возвращать значения всех возможных типов. Все-таки ''Lua'' не зря назван именем небесного тела, обозначающего в символике многих народов переменчивость и обманчивость. В пятой строке мы распечатываем значение, возвращаемое '''foo()&lt;ins style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;'&lt;/ins&gt;'' (при этом, естественно, выполняется сама функция '''foo()'''). Шестая строка выглядит интереснее. В нашем фрагменте, '''foo''' – это переменная, содержащая значение типа «функция» (на самом деле – идентификатор функции, но об этом ниже). В шестой строке мы печатаем значение переменной '''foo''', а не результат, возвращаемый функцией. Вот что мы получим:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt; Привет, я - функция foo()!&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt; Привет, я - функция foo()!&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</description>
			<pubDate>Mon, 20 Sep 2010 05:57:50 GMT</pubDate>			<dc:creator>Crazy Rebel</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF123:Lua</comments>		</item>
		<item>
			<title>Crazy Rebel: викификация, оформление</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF123:Lua&amp;diff=10777&amp;oldid=prev</link>
			<description>&lt;p&gt;викификация, оформление&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая статья&lt;/b&gt;&lt;/p&gt;&lt;div&gt;: '''''Lua''''' Язык программирования сценариев, встраиваемый в ваши приложения&lt;br /&gt;
&lt;br /&gt;
==''Lua'': Функции и объекты==&lt;br /&gt;
&lt;br /&gt;
{{Цикл/Lua}}&lt;br /&gt;
&lt;br /&gt;
: '''Часть 2''': Разобравшись с базовыми возможностями ''Lua'', '''Андрей Боровский''' пробует эмулировать в нем конструкции, знакомые по другим языкам.&lt;br /&gt;
&lt;br /&gt;
На предыдущем уроке мы узнали о существовании ''Lua'' – встраиваемого языка сценариев; мы разобрались, чем он может быть полезен, и рассмотрели примеры написанных на нем простых программ. Мы освоили ввод-вывод и основные управляющие конструкции и познакомились с таблицами – фундаментальным типом данных Lua, лежащим в основе всего мало-мальски сложного (и интересного).&lt;br /&gt;
&lt;br /&gt;
Сегодня мы изучимболее продвинутые возможности ''Lua'', включая реализацию функций объектно-ориентированного программирования (в стандарте языка они отсутствуют). Но сперва изучим один базовый тип данных, не затронутый в прошлый раз.&lt;br /&gt;
&lt;br /&gt;
===Функции ''Lua''===&lt;br /&gt;
&lt;br /&gt;
Давайте рассмотрим такую коротенькую программу:&lt;br /&gt;
&lt;br /&gt;
 function foo()&lt;br /&gt;
  print(“Привет, я - функция foo()!”)&lt;br /&gt;
  return 1&lt;br /&gt;
 end&lt;br /&gt;
 print(foo())&lt;br /&gt;
 print(foo)&lt;br /&gt;
&lt;br /&gt;
Первые четыре строки в пояснениях особо не нуждаются. Ключевое слово '''function''' объявляет функцию. Далее следуют ее имя и список аргументов, заключенный в скобки (у нас он пуст). Тело функции – это блок, обязанный заканчиваться ключевым словом '''end'''. Обратите внимание, что хотя наша функция возвращает значение 1, оператором '''return''', тип возвращаемого значения в ее объявлении не указывается. Аналогично тому, как одна и та же переменная ''Lua'' может принимать значения любых определенных в языке типов, одна и та же функция ''Lua'' может возвращать значения всех возможных типов. Все-таки ''Lua'' не зря назван именем небесного тела, обозначающего в символике многих народов переменчивость и обманчивость. В пятой строке мы распечатываем значение, возвращаемое '''foo()'' (при этом, естественно, выполняется сама функция '''foo()'''). Шестая строка выглядит интереснее. В нашем фрагменте, '''foo''' – это переменная, содержащая значение типа «функция» (на самом деле – идентификатор функции, но об этом ниже). В шестой строке мы печатаем значение переменной '''foo''', а не результат, возвращаемый функцией. Вот что мы получим:&lt;br /&gt;
&lt;br /&gt;
 Привет, я - функция foo()!&lt;br /&gt;
 1&lt;br /&gt;
 function: 00379B20&lt;br /&gt;
&lt;br /&gt;
Первые две строки вывода – результат выполнения выражения '''print(foo())'''. Последняя строка показывает содержимое переменной '''foo'''. Слово '''function''' свидетельствует о том, что она содержит идентификатор функции. Далее следует само значение идентификатора (в нашем случае – 32‑битное шестнадцатеричное число). Возникает соблазн назвать идентификатор адресом функции, но следует помнить, что концепция адресов и указателей в ''Lua'' отсутствует.&lt;br /&gt;
&lt;br /&gt;
Для завершения примера приведем определение функции, которая принимает параметры:&lt;br /&gt;
&lt;br /&gt;
 bar = function(a, b)&lt;br /&gt;
 print(“a+b=”.. a + b)&lt;br /&gt;
 end&lt;br /&gt;
 bar(2,3)&lt;br /&gt;
&lt;br /&gt;
Конструкция&lt;br /&gt;
&lt;br /&gt;
 bar = function(a, b)&lt;br /&gt;
&lt;br /&gt;
эквивалентна&lt;br /&gt;
&lt;br /&gt;
 function bar(a, b)&lt;br /&gt;
&lt;br /&gt;
как, например, в ''JavaScript''. А вот еще один интересный момент:&lt;br /&gt;
&lt;br /&gt;
 function baz()&lt;br /&gt;
 return 1, true, “три”&lt;br /&gt;
 end&lt;br /&gt;
 a,b,c = baz()&lt;br /&gt;
 print(a,b,c) &lt;br /&gt;
&lt;br /&gt;
Да, вы правильно поняли – функции ''Lua'' могут возвращать несколько значений одновременно, причем они могут быть разных типов. Если ваш преподаватель ''С++ ''увидит подобный кусок кода и кинется оборвать вам руки, скажите ему, что вы пишете на ''Lua'', и одну руку, возможно, спасете (вторую он вам все-таки оторвет – за использование интерпретируемых языков).&lt;br /&gt;
&lt;br /&gt;
===Итераторы===&lt;br /&gt;
&lt;br /&gt;
Скажи я вам, что в ''Lua'' нельзя объявить функцию с переменным числом параметров, вы бы наверняка удивились. Увы, удивить мне вас нечем: такие функции в ''Lua'' существуют:&lt;br /&gt;
&lt;br /&gt;
 function sum(...)&lt;br /&gt;
 r = 0&lt;br /&gt;
 for i, v in ipairs(arg) do&lt;br /&gt;
   r = r + v&lt;br /&gt;
 end&lt;br /&gt;
 return r&lt;br /&gt;
 end&lt;br /&gt;
 print(sum(1,2,4,8,16,32))&lt;br /&gt;
&lt;br /&gt;
В этом примере много новых элементов. Троеточие в заголовке функции означает, что число принимаемых аргументов может быть любым. Для передачи переменного числа аргументов используются таблицы, которые, напомню, представляют собой основу всех сложных типов данных в ''Lua''. Увидев троеточие, интерпретатор ''Lua'' автоматически создает таблицу '''arg''', содержащую пары «номер – значение аргумента». Нумерация аргументов начинается с единицы и продолжается непрерывно, так что выражение '''arg[1]''' возвращает первый аргумент, '''arg[2]''' – второй, и т. д.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вспомнив определение оператора '''#''' ([[LXF122:LUA|LXF122]]), вы поймете, что выражение '''#arg''' вернет число аргументов функции. Однако разработчикам ''Lua'' этого показалось мало, и в таблице '''arg''' есть еще одно поле с индексом '''n''', которое содержит число аргументов, так что вместо '''#arg''' можно (и предпочтительно) использовать '''arg.n'''.&lt;br /&gt;
&lt;br /&gt;
Зная все это, мы могли бы использовать уже известную нам форму оператора '''for''' для работы с численными индексами элементов таблицы '''arg''' (предлагаю вам сделать это самостоятельно). Мы же рассмотрим другой вариант, обладающий более широкими возможностями. В общем виде он выглядит так:&lt;br /&gt;
&lt;br /&gt;
 for &amp;lt;список переменных&amp;gt; in &amp;lt;итератор, данные&amp;gt; do&lt;br /&gt;
 ...&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
Функции-итераторы служат для последовательного перебора элементов таблицы и могут использоваться не только в операторе '''for'''. В нашем примере мы используем встроенную функцию '''ipairs()''', которая, будучи совмещена с циклом '''for''', последовательно заполняет две переменные парами значений «индекс аргумента – его значение» (в нашем примере '''i''' содержит индекс элемента '''arg''', а '''v''' – значение индексированного элемента). В результате переменная v последовательно принимает значения всех аргу-ментов (т. е. элементов таблицы '''arg'''). У функции '''ipairs()''' есть брат-близнец '''pairs()''', который оперирует парами «ключ–значение», а не «индекс–значение» (см. врезку).&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Заголовок=Расставим точки над ‘i’|Содержание=Если в рассмотренном нами примере итератор '''ipairs()''' заменить на '''pairs()''', результат выполнения функции '''sum()''' будет другим. Дело в том, что '''ipairs()''' перебирает только индексируемые элементы массива, тогда как '''pairs()''' учтет и '''arg.n'''. Значение этого элемента в нашем примере равно 6, так что вместо ожидаемой суммы 63 мы получим 69.|Ширина=200px}}&lt;br /&gt;
&lt;br /&gt;
Теперь вам явно хочется написать собственный итератор! Давайте реализуем итератор '''bpairs()''', перебирающий элементы массива '''arg''' в обратном порядке. Как ни странно, для этого потребуется объявить не одну, а две функции:&lt;br /&gt;
&lt;br /&gt;
 function backwards(table, count)&lt;br /&gt;
 count = count - 1&lt;br /&gt;
 if table[count] then&lt;br /&gt;
 return count, table[count]&lt;br /&gt;
 end&lt;br /&gt;
 end&lt;br /&gt;
 function bpairs(table)&lt;br /&gt;
 return backwards, table, #table+1&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
Аргументами функции '''backwards()''' должны быть таблица '''table''' и значение '''count''', равное количеству индексируемых элементов плюс 1. Внутри самой функции значение '''count''' уменьшается на 1, и возвращается это уменьшенное значение и соответствующий ему элемент таблицы. Так будет происходить до тех пор, пока '''table[count]''' не окажется равным '''nil'''. Если вам кажется, что с функцией '''backwards()''' не все так просто, читайте врезку.&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Заголовок=Повышенная передача|Содержание=Параметры-переменные функций ''Lua'' передаются не по значению, а по ссылке. Таким образом, изменение значения любого аргумента внутри функции приводит к изменению этого значения и за ее пределами. Этим фактом мы и пользуемся в функции '''backwards()'''.|Ширина=200px}}&lt;br /&gt;
&lt;br /&gt;
Функция '''bpairs()''' работает и того проще. Она возвращает три вещи: саму функцию '''backwards()''' и значения аргументов для ее первого вызова. Оператор '''for''' вызывает функцию '''backwards()''', используя «для затравки» значения, полученные от '''bpairs()''', до тех пор, пока '''backwards()''' возвращает результат. Если вы не поняли это место, не пугайтесь: сейчас будет еще один наглядный пример. Теперь мы можем заменить строку&lt;br /&gt;
&lt;br /&gt;
 for i, v in ipairs(arg) do&lt;br /&gt;
&lt;br /&gt;
строкой&lt;br /&gt;
&lt;br /&gt;
 for i, v in bpairs(arg) do&lt;br /&gt;
&lt;br /&gt;
Аргументы функции '''sum()''' будут перебираться в обратном порядке, в чем можно убедиться, вставив в цикл вызов '''print(i,v)'''. Сам результат от перемены мест слагаемых не изменится.&lt;br /&gt;
&lt;br /&gt;
Зная, как работают функции-итераторы, мы можем воспроизвести механику оператора '''for''' и без обертки '''bpairs()''':&lt;br /&gt;
&lt;br /&gt;
 for i, v in backwards, arg, #arg+1 do&lt;br /&gt;
 r = r + v&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
 for i, v in backwards, arg, arg.n+1 do&lt;br /&gt;
 r = r + v&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
В принципе, функция '''bpairs()''' нам не нужна. Это просто удобство, позволяющее написать одно выражение вместо трех.&lt;br /&gt;
&lt;br /&gt;
===Чего только нет===&lt;br /&gt;
&lt;br /&gt;
Как и в любых других блоках, в теле функции можно объявлять локальные переменные, видимые только внутри нее. В отличие от ''C/C++'', эти переменные нельзя объявить статическими, то есть сделать так, чтобы они хранили данные в перерывах между вызовами функции. Впрочем, статические локальные переменные можно эмулировать. Вот одно из возможных решений:&lt;br /&gt;
&lt;br /&gt;
 do&lt;br /&gt;
  local loc=0&lt;br /&gt;
  function fred(a)&lt;br /&gt;
   loc=loc+a&lt;br /&gt;
   return loc&lt;br /&gt;
  end&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
Переменная '''loc''' объявлена как локальная, и за пределами блока '''do...end''' видна не будет. Функция '''fred()''', напротив, не локальная, и ее можно вызывать за пределами блока. Поскольку переменная '''loc''' объявлена вне блока функции '''fred()''', она будет существовать в перерывах между вызовами '''fred()''', но поскольку '''loc '''локальна для блока, в котором определена функция '''fred()''', никто, кроме '''fred()''', не сможет получить к ней доступ.&lt;br /&gt;
&lt;br /&gt;
Нет в синтаксисе ''Lua'' и концепции параметра со значением по умолчанию (как в ''C++''), но и тут нам на помощь приходит хакерская изобретательность:&lt;br /&gt;
&lt;br /&gt;
 function defval(v)&lt;br /&gt;
 v = v or 'default value'&lt;br /&gt;
 return v&lt;br /&gt;
 end&lt;br /&gt;
 print(defval())&lt;br /&gt;
 print(defval(‘Мое значение’))&lt;br /&gt;
&lt;br /&gt;
То, что при объявлении функции указан список параметров, не означает, что соответствующие им значения необходимо вводить при вызове. Если параметру функции не сопоставлено значение, он будет равен '''nil'''. Смысл строки&lt;br /&gt;
&lt;br /&gt;
 v = v or 'default value'&lt;br /&gt;
&lt;br /&gt;
можно перевести так: если '''v''' не равно '''nil''', присвоить '''v''' значение '''v''', иначе присвоить '''v''' значение ''''default value''''. Оператор '''or''' ведет себя здесь не так, как при работе с логическими значениями, а как краткая форма '''if'''. Таким образом, если при вызове '''defval()''' мы не указываем '''v''', в теле функции ему назначается значение по умолчанию. В противном случае используется значение, переданное через '''v'''.&lt;br /&gt;
&lt;br /&gt;
Поскольку функции, определенные в ''Lua'' – это не блоки машинного кода, намертво скомпонованные с основной программой, а структуры данных, предназначенные для интерпретатора, их можно удалять (высвобождая тем самым оперативную память).&lt;br /&gt;
&lt;br /&gt;
Например, строка&lt;br /&gt;
&lt;br /&gt;
 backwards = nil&lt;br /&gt;
&lt;br /&gt;
удаляет функцию '''backwards()'''. Тут, правда, есть один тонкий момент. Рассмотрим фрагмент&lt;br /&gt;
&lt;br /&gt;
 foo = backwards&lt;br /&gt;
&lt;br /&gt;
После первого присваивания идентификатор '''foo''' можно использовать так же, как идентификатор '''backwards'''. Например:&lt;br /&gt;
&lt;br /&gt;
 for i, v in foo, arg, arg.n+1 do&lt;br /&gt;
&lt;br /&gt;
При этом мы не делаем из одной функции две. Как было сказано выше, у каждой определенной нами функции есть численный идентификатор, который и копируется в процессе присваивания. Если теперь мы напишем&lt;br /&gt;
&lt;br /&gt;
 backwards = nil&lt;br /&gt;
&lt;br /&gt;
переменная '''backwards''' перестанет указывать на функцию, а '''foo''' – не перестанет. В результате память, занятая функцией, освобождена не будет. Уследить за тем, чтобы ни одна переменная не содержала идентификатор функции (а только в этом случае произойдет ее удаление) очень сложно. Эту задачу выполняет автоматический сборщик мусора. Контрольный вопрос: при каких условиях сборщик мусора сможет удалить переменную '''loc''' из примера с функцией '''fred()'''? Ответ: когда будут удалены все ссылки на '''fred()'''.&lt;br /&gt;
&lt;br /&gt;
Думаю, что за время чтения этого раздела вы получили столько информации о функциях ''Lua'', что ее требуется переварить. Когда процесс закончится, вспомните то, что будет наиболее важным для следующего раздела: численные идентификаторы функций являются простыми значениями, которые могут присваиваться любым переменным, в том числе, элементам таблиц.&lt;br /&gt;
&lt;br /&gt;
===Объекты в ''Lua''===&lt;br /&gt;
&lt;br /&gt;
Родные объекты в ''Lua'' отсутствуют, и нам придется их эмулировать. Гибкость синтаксиса это позволяет, но прежде необходимо понимать основы реализации ООП в других языках. В первом приближении, объект – это совокупность структур данных и методов для оперирования ими. Если структура данных и набор методов у двух объектов совпадают, эти объекты могут принадлежать (а могут и не принадлежать) одному классу. Как правило, в программе используется несколько объектов одного класса. Для каждого из них создается своя область данных (чем же иначе объекты будут отличаться друг от друга?), но для ее обработки у всех объектов одного класса используются (физически) одни и те же методы. Каким образом метод, который мало чем отличается от обычной функции, узнает, с какой именно структурой данных ему предстоит работать? Для этой цели у него есть скрытый параметр (в одних языках он называется '''this''', в других – '''self''', в третьих – '''dontuseme'''), который представляет собой указатель на структуру данных того объекта, для которого вызывается метод.&lt;br /&gt;
&lt;br /&gt;
Этих неполных и неформальных понятий нам пока будет достаточно. Педанты могут обратиться к теории ООП, но предупреждаю, что теорий существует несколько, и все они насыщены весьма сложными абстрактными понятиями, взятыми из алгебры и теории множеств.&lt;br /&gt;
&lt;br /&gt;
С учетом изложенного выше, давайте рассмотрим определение объекта '''Employee''' (сотрудник).&lt;br /&gt;
&lt;br /&gt;
 Employee = {name = '', age = 0, salary = 0, position = ''}&lt;br /&gt;
 function Employee.incAge(self)&lt;br /&gt;
  self.age = self.age + 1&lt;br /&gt;
 end&lt;br /&gt;
 function Employee.scaleSalary(self, factor)&lt;br /&gt;
  self.salary = self.salary*factor&lt;br /&gt;
 end&lt;br /&gt;
 function Employee.print(self)&lt;br /&gt;
  print(self.name, 'age: '..self.age, 'salary: '..self.salary, 'position: '..self.position)&lt;br /&gt;
 end;&lt;br /&gt;
 Employee.name = 'Vasya Pupkin'&lt;br /&gt;
 Employee.age = 25&lt;br /&gt;
 Employee.position = 'Manager'&lt;br /&gt;
 Employee.salary = 1000&lt;br /&gt;
 Employee:incAge()&lt;br /&gt;
 e = Employee&lt;br /&gt;
 Employee = nil&lt;br /&gt;
 e:scaleSalary(2);&lt;br /&gt;
 e:print()&lt;br /&gt;
&lt;br /&gt;
У объекта (таблицы) '''Employee''' есть четыре поля данных, имена которых говорят сами за себя. Кроме того, для объекта '''Employee''' определено три метода: '''incAge()''', увеличивающий значение поля '''age''' на единицу, '''scaleSalary()''', умножающий поле '''salary''' на заданный коэффициент (желательно – больший единицы) и '''print()''', выводящий сведения о сотруднике.&lt;br /&gt;
&lt;br /&gt;
Обращаю ваше внимание на то, что конструкция&lt;br /&gt;
&lt;br /&gt;
 function Employee.print(self)&lt;br /&gt;
&lt;br /&gt;
эквивалентна&lt;br /&gt;
&lt;br /&gt;
 Employee.print = function (self)&lt;br /&gt;
&lt;br /&gt;
Мы просто создаем еще один элемент таблицы '''Employee''' со значением типа «функция». Практически все элементы синтаксиса в представленном фрагменте вам уже знакомы. Новшеством является только выражение типа&lt;br /&gt;
&lt;br /&gt;
 Employee:incAge()&lt;br /&gt;
&lt;br /&gt;
Оператор ''':''' означает, что первый аргумент вызываемой функции – ссылка на таблицу, имя которой расположено слева от оператора (напомню, все переменные-параметры в ''Lua'' передаются по ссылке). В данном случае, без ''':''' можно и обойтись, написав&lt;br /&gt;
&lt;br /&gt;
 Employee.incAge(Employee)&lt;br /&gt;
&lt;br /&gt;
Однако такая форма записи более громоздка и не всегда верна: например, она не сработает при использовании полиморфизма.&lt;br /&gt;
&lt;br /&gt;
Оператор : применим не только при вызове, но и при объявлении методов. Например, вместо&lt;br /&gt;
&lt;br /&gt;
 function Employee.print(self)&lt;br /&gt;
&lt;br /&gt;
можно написать&lt;br /&gt;
&lt;br /&gt;
 function Employee:print()&lt;br /&gt;
&lt;br /&gt;
A вместо&lt;br /&gt;
&lt;br /&gt;
 Employee.setName(self, name)&lt;br /&gt;
&lt;br /&gt;
использовать&lt;br /&gt;
&lt;br /&gt;
 Employee::setName(name)&lt;br /&gt;
&lt;br /&gt;
Параметр '''self''' для функций будет создан автоматически.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим четыре последних строки программы. Переменной '''e''' присваивается ссылка на объект '''Employee''', а '''Employee''' устанавливается в '''nil'''. Тут демонстрирует свою полезность параметр '''self''': будь в методах объекта '''Employee''' зашита ссылка на '''Employee''', после выполнения операций они перестали бы работать (ведь переменная '''Employee''' будет содержать '''nil'''). Параметр '''self''' позволяет использовать методы объектов, не заботясь об имени переменной, которой присвоена ссылка на объект.&lt;br /&gt;
&lt;br /&gt;
Ну, а как создавать экземпляры объекта '''Employee'''? Для этого задействуем мета-таблицы. Мета-таблицами в ''Lua'' именуются таблицы, описывающие правила обращения с некоторым значением – в том числе с другими таблицами. Вот как может выглядеть мета-таблица для объектов '''Employee''':&lt;br /&gt;
&lt;br /&gt;
 function Employee:new (name, age, salary, position)&lt;br /&gt;
   obj = {name = name, age = age, salary = salary, position = position}&lt;br /&gt;
   setmetatable(obj, self)&lt;br /&gt;
   self.__index = self&lt;br /&gt;
   return obj&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
В результате можно будет написать:&lt;br /&gt;
&lt;br /&gt;
 e1 = Employee:new('Vasya Pupkin', 25, 1000, 'manager')&lt;br /&gt;
 e2 = Employee:new('Ivan Petrov', 31, 1500, 'accountant')&lt;br /&gt;
&lt;br /&gt;
и убедиться, что вызовы '''e1:print()''' и '''e2:print()''' выдают информацию о двух разных сотрудниках.&lt;br /&gt;
&lt;br /&gt;
Я понимаю, что от синтаксических выкрутасов ''Lua'' вы уже готовы лезть на стену. Но, как говорят католики из Рио-де-Жанейро, «Терпение и труд все перетрут». Сейчас мы все поймем.&lt;br /&gt;
&lt;br /&gt;
===Метамагия===&lt;br /&gt;
&lt;br /&gt;
Прежде всего, '''new''' – это обычный элемент-функция таблицы '''Employee'''. В ней создается новая таблица '''obj''' с четырьмя элементами, значения которых берутся из параметров функции. Строка&lt;br /&gt;
&lt;br /&gt;
 setmetatable(obj, self)&lt;br /&gt;
&lt;br /&gt;
провозглашает, что '''Employee''' – мета-таблица для таблицы '''obj'''. Теперь при выполнении над '''obj''' нестандартных операций (например, индексации несуществующих элементов) таблица '''obj''' будет неявно обращаться к мета-таблице '''Employee''' за описанием необходимых действий. Еще интереснее строка&lt;br /&gt;
&lt;br /&gt;
 self.__index = self&lt;br /&gt;
&lt;br /&gt;
Она означает, что если при работе с '''obj''' произойдет обращение к элементу, отсутствующему в таблице, '''Lua''' будет искать элемент с соответствующим ключом в мета-таблице. Заметьте, что, создавая таблицу '''obj''', мы не указывали методов, а значит, вызов&lt;br /&gt;
&lt;br /&gt;
 e1:print()&lt;br /&gt;
&lt;br /&gt;
обратится к элементу '''Employee.print'''. Благодаря параметру '''self''' метод '''Employee.print''' будет работать с данными объекта '''e1''', а не&lt;br /&gt;
'''Employee'''. Кстати, теперь присвоение переменной '''Employee''' значения '''nil''' аннулирует методы всех объектов, созданных с помощью '''Employee:new()''': ведь их описания исчезнут вместе с мета-таблицей. Как вы уже поняли, при работе с объектами мета-таблица играет роль класса. Стало быть, в ''Lua'' можно удалить не только данные объекта, но и код его методов (но вашему преподавателю по ''C++'' об этом молчок).&lt;br /&gt;
&lt;br /&gt;
А можем ли мы написать такое?&lt;br /&gt;
&lt;br /&gt;
 function Employee:new (name, age, salary, position)&lt;br /&gt;
  obj = {name = name, age = age, salary = salary, position = position, print = self.print}&lt;br /&gt;
  setmetatable(obj, self)&lt;br /&gt;
  self.__index = self&lt;br /&gt;
  return obj&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
Да, можем, и тогда при вызове метода '''print()''' объекту '''obj''' не придется обращаться к мета-таблице. Но наш код потеряет гибкость. Если в ходе выполнения программы описание метода '''print()''' в мета-таблице изменится, ранее созданные объекты об этом не узнают: ведь у них уже есть свое поле '''print''', и обращаться к мета-таблице им незачем. Можно, наоборот, полностью перенести описание объекта (не только методов, но и полей) в мета-таблицу. Для этого перепишем функцию '''Employee:new()''' так:&lt;br /&gt;
&lt;br /&gt;
 function Employee:new (obj)&lt;br /&gt;
   obj = obj or {}&lt;br /&gt;
   setmetatable(obj, self)&lt;br /&gt;
   self.__index = self&lt;br /&gt;
   return obj&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
Тогда синтаксис вызова функции '''Employee:new()''' тоже изменится:&lt;br /&gt;
&lt;br /&gt;
 e1 = Employee:new{name = 'Vasya Pupkin', age = 25, salary = 1000, position = 'manager'}&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на скобки. Этот вариант кажется неудобным: по сути, объект '''obj''' конструируется «вручную» и приходится явно указывать имена полей, уже определенных в мета-таблице. Зато легко организовать наследование классов. Пусть нужно создать объект-потомок класса '''Employee''' с переопределенным методом '''print()'''. Вот что для этого требуется:&lt;br /&gt;
&lt;br /&gt;
 function newPrint(self)&lt;br /&gt;
  print('name: '..self.name..' age: '..self.age..' salary: '..self.salary..'position: '..self.position)&lt;br /&gt;
 end&lt;br /&gt;
 e3 = Employee:new{name = 'Ivan Sidorov', age = 20, salary = 800, position='security manager', print = newPrint}&lt;br /&gt;
 e3:print()&lt;br /&gt;
&lt;br /&gt;
Создавая объект '''e3''', мы заменяем функцию '''Employee:print()''' на '''newPrint()'''. В результате при вызове '''e3:print()''' на самом деле будет вызвана функция '''newPrint()''' – одним махом мы получаем не только наследование, но и, в некотором смысле, полиморфизм. Тем же способом можно добавлять в объекты-потомки '''Employee''' новые поля данных и методы, не меняя описания мета-таблицы '''Employee'''.&lt;br /&gt;
&lt;br /&gt;
Мы подошли к важной мысли: скрипты ''Lua'' способны само-модифицироваться, а значит, быть самообучаемыми! Я не упоминал об этом достоинстве ''Lua'' – не буду врать, что просто забыл, скажу честно: новичкам, не прошедшим вторую стадию посвящения, знать о таком было рано. А что же дальше-то будет?! '''LXF'''&lt;/div&gt;</description>
			<pubDate>Mon, 20 Sep 2010 05:57:14 GMT</pubDate>			<dc:creator>Crazy Rebel</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF123:Lua</comments>		</item>
	</channel>
</rss>