|
Программирование на Lua |
|
| Часть I. Язык Глава 3. Выражения |
Конструкторы - это выражения, которые создают и инициализируют таблицы. Они являются отличительной особенностью Lua и одним из наиболее удобных и универсальных механизмов.
Простейшей формой конструктора является пустой конструктор,{}, создающий пустую таблицу; мы уже встречали их ранее. Также, конструкторы могут инициализировать массивы (called also sequences or lists). Например, the statement
days = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
инициализирует days[1] строкой "Sunday" (первый элемент всегда имеет индекс 1, а не 0), days[2] строкой "Monday" и т.д.:
print(days[4]) --> Wednesday
Конструкторы могут использовать не только константные выражения. При задании значений элементов можно использовать любые виды выражений. Например, можно построить небольшую таблицу синусов:
tab = {sin(1), sin(2), sin(3), sin(4),
sin(5), sin(6), sin(7), sin(8)}
Для инициализации таблицы как записи, Lua предоставляет следующий синтаксис (т.н. record-style форма конструктора, прим. переводчика):
a = {x=0, y=0}
что эквивалентно
a = {}; a.x=0; a.y=0
Не имеет значения, какой конструктор использовался для создания таблицы, всегда есть возможность добавлять и удалять другие поля любого типа:
w = {x=0, y=0, label="console"}
x = {sin(0), sin(1), sin(2)}
w[1] = "another field"
x.f = w
print(w["x"]) --> 0
print(w[1]) --> another field
print(x.f[1]) --> another field
w.x = nil -- удаление поля "x"
То есть, все таблицы создаются одинаковыми; конструкторы влияют только на инициализацию.
Every time Lua evaluates a constructor, it creates and initializes a new table. Следовательно, можно использовать таблицы для реализации связных списков (list-style форма конструктора, прим. переводчика):
list = nil
for line in io.lines() do
list = {next=list, value=line}
end
Данный код считывает строки со стандартного входного потока и сохраняет их в связном списке в обратном порядке. Каждый узел списка представляет собой таблицу с двумя полями: value, которое содержит собственно считанную строку, и next, являющееся указателем на следующий узел. Следующий код выводит содержимое списка:
l = list
while l do
print(l.value)
l = l.next
end
(Т.к. список организован по принципу стека (LIFO), строки будут выведены в обратном порядке.) Несмотря на поучительность примера, данная реализация практически не используется в реальных программах на Lua; списки лучше реализовывать как массивы, как можно будет увидеть в Главе 11.
Стили инициализации (record- и list-style) можно применять одновременно в одном конструкторе:
polyline = {color="blue", thickness=2, npoints=4,
{x=0, y=0},
{x=-10, y=0},
{x=-10, y=1},
{x=0, y=1}
}
Приведенный пример также иллюстрирует способ создания вложенных конструкторов для представления более сложных структур данных. Каждый элемент polyline[1], ..., polyline[4] является таблицей, представляющей запись:
print(polyline[2].x) --> -10
Тем не менее, обе формы конструктора имеют свои ограничения. Например, нельзя инициализировать поля с отрицательными индексами, или со строковыми индексами that are not proper identifiers. Для этих целей существует другой, более общий, формат. В этом формате инициализируемый индекс явно указывается в квадратных скобках, являясь одним из операндов выражения:
opnames = {["+"] = "add", ["-"] = "sub",
["*"] = "mul", ["/"] = "div"}
i = 20; s = "-"
a = {[i+0] = s, [i+1] = s..s, [i+2] = s..s..s}
print(opnames[s]) --> sub
print(a[22]) --> ---
Этот синтаксис более громоздкий, но и более гибкий: и list-style и record-style являются вариантами этой общей формы. Конструктор
{x=0, y=0}
эквивалентен
{["x"]=0, ["y"]=0}
а конструктор
{"red", "green", "blue"}
эквивалентен
{[1]="red", [2]="green", [3]="blue"}
Если есть реальная необходимость, чтобы массивы начинались с индекса 0, несложно написать следующее:
days = {[0]="Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
Теперь первое значение, "Sunday", имеет индекс 0. Этот ноль не влияет на другие поля, но "Monday", естественно, имеет индекс 1, т.к. следует первым в списке значений конструктора; следующие значения располагаются за ним. Тем не менее, я не рекомендую использовать в Lua массивы, начинающиеся с 0. Помните, что большинство функций считают, что массивы начинаются с индекса 1, и следовательно обрабатывают такие массивы некорректно.
Допускается после последнего элемента помещать запятую. Эта завершающая запятая не является обязательной, но всегда корректна:
a = {[1]="red", [2]="green", [3]="blue",}
Такая гибкость позволяет легко создавать программы, формирующие таблицы Lua, т.к. нет необходимости обрабатывать последний элемент особым образом.
Наконец, в конструкторе в качестве разделителя всегда можно использовать точку с запятой (;) вместо запятой (,). Мы обычно используем ';' для разделения различных секций конструктора, например для разделения его list-style части от record-style части:
{x=10, y=45; "one", "two", "three"}
| Copyright © 2003-2004 Roberto Ierusalimschy. All rights reserved. | Перевод на русский Artem13 |
|