← Основы Глава Конструкции языка →
Типы данных


Еще раз о Lua

править

Мы говорили, что Lua сильно похож на Python. Еще одна общая особенность для двух данных языков- динамическая типизация. Динамическая типизация в данных языках позволяет изменить типы переменных: из числа можно сделать строку, а из строки число; а сами типы переменных определяются интерпретатором (в строго типизированных языках, тип переменной надо указывать заранее: в C++ целочисленную переменную можно создать таким образом - int a = 5).

Сейчас мы разберемся подробнее, какую роль играет типизация и как работать с переменными.

Переменная какого типа?

править

Чтобы определить к какому типу относится переменная, можно использовать функцию type(). О функциях мы узнаем позже, но думаю, вы уже видите некоторые параллели функций type() и print(). И так, пример:

type(15) --> number
type(str) --> nil 
type ("str") --> string

Как мы видим, функция type() говорит нам, что наши значения - число, nil, строка соответственно.

Интересное замечание: сама функция type() возвращает строку

Типы данных в Lua

править

В Lua есть 8 базовых (то есть встроенных в сам язык) типов: number(число), string(строка), boolean(логический тип), nil (тип "ничего"), table,function, userdata, thread. Пришло время разобраться в каждом из них.

number(число)

править

Что разительно отличает Lua от других языков - существование только одного типа данных для чисел и это - number. Любые числа будут number:

-- Пример 1
a = 5
type(a) --> number
-- Пример 2
a = 5.111 --> 5,111 индентично
type(a) --> number
-- Пример 3
a = 5E+6 --> 5e+6 индентично
type(a) --> number
print(a) --> 5 000 000

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

Второй пример: float или double (то есть число с плавающей точкой) в других языках. Заметим, что различие между точкой и запятой в Lua не делается - это все числа.

Числа можно записывать не в десятичной системе счисления, но и в шестнадцатеричной:

type(0xFFFF) --> number
print(0xFFFF) --> 65535

Как мы уже писали ранее, тип nil является ничем. Он создан для того, что показывать, что в переменной нет ничего (а как мы видели ранее, "нет переменной").

print(a) --> nil, так как переменной а - не существует
a = 5
print(a) --> 5
a = nil --> эксперимент: зададим другое значение a
print(a) --> nil

Посмотрите на строку 4: здесь проявилось свойство динамической типизации - мы смогли превратить "a" у которой был тип number в "а", у которой тип nil. В таких языках как Lua, Python, Ruby, переопределить переменную достаточно просто. В отличии от C++ или Java.

А теперь посмотрите на последнюю, 5-ую строку: переопределив "а" в nil, мы, фактически, уничтожили переменную "a".

Тип string представляет из себя последовательность символов (в других языках программирования символы имеют свой тип - char). Под символами (опять же, в других языках программирования) подразумевается одна буква, например, "l", "u" или "a". Вместе они образуют слово "lua", которое имеет тип string. Но вернемся к Lua: в этом языке нет типа char, а значит любое слово (обрамленное одинарными или двойными кавычками) будет является string.

Мы разобрались с тем, что одинарные и двойные кавычки работают только в одной строке. Но как нам разместить многострочный текст? Для этого используются [[ ... ]]

a = "Wiki
books" --> здесь произойдет ошибка

a = [[
Wikipedia
and
Wikibooks
]] --> отобразится корректно

Заметим, что string является неизменяемым типом: вы не можете заменить определенный символ в строке на другой. Тем не менее, вы можете работать над строкой используя функции:

a = "Wiki Media"
b = string.gsub(a,"Media", "Book") -- Фрагмент, в переменной а, "Media" заменяем на Book
print(b) --> Wiki Book

Вы можете складывать строки при помощи ..:

print("Wiki" .. "Books") --> WikiBooks

Часто приходится работать с длинной строки. Чтобы получить длину, можно использовать #переменная:

print(#"Wiki") --> 4
a = "Wikibooks"
print(#a) --> 9

Экранированные последовательности

править
Символ Описание
\n Перевод на новую строку

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

Истина возращается тогда, когда выражение является истинным (2+2 == 4 или 2+2 ~= 4), ложь является противоположным значением.

Тип table переставляет из себя ассоциативный массив (или словарь, если сравнивать с Python). Под массивом, мы могли бы понимать некий набор переменных. А если же говорить о ассоциативном массиве, то у переменных есть "ассоциация", то есть название, или правильнее - ключ. Есть еще последовательность: массив, ключи которого представляют из себя последовательные натуральные числа (от 1 до n, где n - последний ключ массива). В этом случае правильно было бы сказать, что в последовательности ключи являются индексами.

Чтобы создать тип table надо использовать конструкцию {}:

array = {} --> Только что мы создали пустой массив, в котором ничего нет

Теперь попробуем добавить какой-нибудь элемент в него:

array = {}
array[1] = 15 --> ключу 1 соответствует значение 15
array["x"] = 89 --> ключу "x" соответствует значение 89

Мы добавили в него два элемента. Теперь наша таблица (или массив) содержит два элемента: ключ 1 с значением 15 и ключ "х" с значением 89. Давайте проверим, так ли это:

array[1] --> 15
array["x"] --> 89

Ранее говорилось, что элементы массива представляют из себя переменные (например, переменная array[1] с ключом 1 и значением 15), а значит, можно работать с ними как с переменными:

Word = {}
Word[1] = "Wiki"
Word[2] = "pedia"
-- Ниже - обычное сложение строк 
print(Word[1] .. Word[2]) --> Wikipedia

Заметим еще важную роль nil в таблицах: если вы не создали элемент массива (например, Word[3]), то его вызов (например, с использованием print()) вернет вам nil. Мы также можем назначит значение nil любому элементу массива, тем самым уничтожив его. Еще одно важное замечание: если вы сделаете ключ типа string, то вы можете иначе вызывать элемент массива, с использованием формы НазваниеМассива.Ключ:

a = {}
a["arr"] = 15
print(a.arr) --> 15

Хочу заметить, что такой способ можно использовать только когда вы представили ключ в виде строки. Символ # представляет из себя длину строки. Но в случае с таблицей - это ее длина (а вернее, количество элементов в ней). Соответственно, мы можем совершать следующие операции:

-- Пусть есть некий массив а 
a = {}
print(a[#a]) --> напечает последний элемент (#a - значение последнего индекса для массива)
a[#a+1] = 15 --> добавляет еще один элемент в конец массива

Существуют и другие способы создания таблицы. Первый из них - создание последовательности(или его еще называют - список):

a = {"Wikipedia", "Wikibooks", "Wikiversity"} --> Создали в этот раз непустой список
print(a[2]) --> Wikibooks

Хочу заметить, что счет индексов в Lua начинается с 1, а не с 0 как в других языках программирования: именно поэтому мы получили Wikibooks,а не Wikiversity как могло произойти в других языках программирования. Можно создать и такой массив с ключ/значением (их в Python называют словарем):

array = {a = 1, b = 2}
print(array[b]) --> nil

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

array = {{a,b},{c,d}} --[[ массив из двух массивов (подмассивов) содержащие а и b 
в одном и c и d в другом--]]
print(array[1][1]) --[[a так как мы сначала выберем первый массив {a,b} а из него
первый элемент a --]]

Мы разобрали наиболее элементарные типы данных в Lua. Другие типы данных являются специфичными и мы их рассмотрим в следующих главах.

Приведение типов.

править

Как мы говорили ранее, Lua - язык программирования с динамической типизацией. Это значит, что вы можете изменять типы данных для переменной, можете явно не указывать тип данных. Тем не менее, не всегда можно совершать операции над разными типами данных. Мы бы сформулировали следующие правила:

  1. number + number = number: если мы сложим 2+2, то получим 4:
    print(5+5) --> 10
    
  2. string + string = number, если в строке есть цифры: если мы сложим "2" + "2", то получим 4, но мы не можем сложить "Wiki" + "Media"
  3. Строка не складывается ни с кем, если она содержит буквы: дублирует прошлое правило, то есть, мы не можем сложить "Wiki" + 2

Бывают случаи, когда вам надо преобразовать число в строку или наоборот. Для этого используют две функции tonumber() (в число) и tostring() (в строку). Очевидно, что с tostring() проблем возникнуть не должно - мы любое число всегда можем перевести в строку, а вот проблемы с tonumber() могут возникнуть, если мы захотим, к примеру, "Wiki" перевести в число. Именно поэтому, в таких случаях, tonumber() возвращает nil, то есть, ничто, чтобы показать, что это действие выполнить невозможно:

a = tostring(154) --> 154 станет "154"
type(a) --> string
print(a) --> 154 (из строки выше, мы понимаем, что это строка)

type(tonumber("154")) --> number

tonumber("Wiki") --> nil

Действия с переменными

править

С переменными (а вернее, только с числовыми переменными) можно проводить арифметические операции:

a = 5 
b = 10 
-- Сложение
print(a + b) --> 15
-- Отрицание
print(a - b) --> -5
-- Умножение
print(a * b) --> 50
-- Деление
print(b/a) --> 2 
-- Деление от остатка
print(5%2) --> 1 (если вы разделите 5 на 2 вы получите 2 с остатком 1)
-- Возведение в степень
print(5^2) --> 25
-- Корень от числа
print(25^(1/2)) --> 5

Операции сравнения всегда возвращают значение типа boolean:

a = 5
b = 10 
-- Строго больше/меньше
print(a>b) --> false (на самом деле b > a)
-- Больше/меньше или равно (в отличии от прошлого, равенство вернет true)
print(b >= a) --> true
print(b >= b) --> true
print(b > b) --> false
-- Равенство
print(b == b) --> true
-- Неравенство 
print(b ~= b) --> false

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

print("a" < "b") --> true

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

  • ^
  • -, not, #
  • %,*,/
  • +,-
  • ..
  • Операции сравнения
  • and
  • or

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

Логические операции

править

В Lua существует 3 логические операции: and (и), or(или), not (нет). Результат этих операций - false или true. Фактически, они сравнивают истинность двух выражений. Первое, что мы рассмотрим - and: возвращает true, только когда оба выражения являются истинными:

print(true and true) --> true (и то, и то - истина)
print(true and false) --> false (истина только одна, а должны быть истины все)

Второе - or. Он возвращает true, когда хотя бы одно из выражений является истинной:

print(true or false) --> true (одна истина уже есть)
print(false or false) --> false (нет истинного выражения)

И третье - not. Возвращает отрицание выражения: если исходное выражение было истиной, то благодаря not оно станет ложным:

print(not true) --> false

А теперь сделаем некоторые замечания: под true/false могут быть любые выражения, которые могут оказаться boolean, например: 2+2 == 4. Второе важное замечание - это приоритеты выражений: если мы захотим сравнить что-то более сложное, например a and b or c, то у нас могут возникнуть проблемы из-за интерпретации этого выражения. And всегда имеет больший приоритет над or: сначала будет сравниваться b или с, а потом итог прошлого выражения и а. Чтобы сделать выражение иным, мы можем использовать такую запись: (a and b) or c.



← Основы Конструкции языка →