Блог им. morefinances

Qlua: основы, часть 1

Весь материал, который здесь и далее будет рассматриваться по qlua, работает на 10й версии квика. Вполне допускаю, что со временем какие-то функции разработчики перепишут и в новых версиях что-то нужно будет сверять c мануалами, уточнять хелпом и на форумах, но предполагаю, что а) эти изменения будут вводиться очень не быстро и б) синтаксис и основа при этом останутся без существенных изменений.

Сегодня рассмотрим:

  • message
  • конкатенация
  • фильтрация по сообщениям в терминале
  • PrintDbgStr
  • комментарии
  • типы данных
  • type
  • операции с числами
  • операции со строками
  • операции с таблицами
  • условные операторы

 

message

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

Особенности message: функция после вывода делает перенос строки, поэтому если необходимо вывести несколько значений в одной строке нужно делать их слияние (об этом ниже).

Для корректного отображения русских букв необходимо выбирать котировку файла Windows-1251 (об этом также в прошлый раз мы уже говорили). Иногда по этой причине некоторые разработчики пишут только на английском весь вывод текста в терминал, чтобы не заморачиваться с кодировкой, в т.ч. при размещении на github и совместной работе с кодом.

Кавычки могут быть либо одинарными ‘, либо двойными ”. Допускается одновременное использование обоих типов кавычек, когда необходимо что-то выделить ими в тексте:

message(«ООО 'Арка Текнолоджиз ' …») или message('ООО «Арка Текнолоджиз » …').

Хотя по мне более корректной является вставка символа через обратный слеш \' или \" соответственно.

message выводит не более 899 символов.

Можно поставить одну из 3 иконок в сообщении:

message(text, 1) –  по умолчанию, обычное сообщение с иконкой Qlua: основы, часть 1. Равносильно записи message(text).

message(text, 2) – с иконкой Qlua: основы, часть 1

message(text, 3) – с Qlua: основы, часть 1

При этом функция принимает для вывода только строковые данные, т.е. если запустить код

 

abc = 123
message(abc)

то терминал не выдаст ошибку, но и ничего не напишет.


Чтобы message вывел число:

A) необходимо либо перевести его в строку (делается через tostring):

abc = 123
message(tostring(abc))

B) либо воспользоваться склеиванием с другой строкой (так называемая конкатенация).

 

Конкатенация.

Объединение чисел и строк в одну строку. В lua осуществляется с помощью двух точек  .. : a = b..c

abc = 123
message('abc='..abc)

Хотя более корректная запись была бы message('abc='..tostring(abc)), но и без tostring терминал сделает присоединение корректно.

Еще пример:

text_one = 'one '
text_two ='two'
text_sum = text_one..text_two
message(text_sum)


Фильтрация по сообщениям в терминале

Удобно в дальнейшем в начале каждого скрипта задавать краткое название алгоритма, например

progname='program 5:'

А весь дальнейший вывод осуществлять через message(progname..text)            

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

 Qlua: основы, часть 1

Не забудьте после этого убрать фильтр, иначе в таблице так и будут показываться только сообщения от соответствующего скрипта.


Если сообщений очень много, то чтобы не грузить терминал, используется либо вывод в файл *.csv (об этом поговорим позже), либо делается аналогичный вывод через PrintDbgStr в DebugView (необходимо предварительно скачать).


Синтаксис аналогичный message:

abc = 123
PrintDbgStr ("abc="..abc)

Перевод из строки в число осуществляется с помощью tonumber

a = 1
b = "2"
sum = a + tonumber(b)
message(tostring(sum))

Из-за того, что конструкция message(tostring()) получается достаточно громоздкой разработчики иногда пишут свою функцию вывода, которая может принимать, например, и числа, и строки, и таблицы. Особенно это актуально для вывода числовых данных, полученных из квика, которые (как мы увидим позже) содержат порой большое количество ненужных нулей после запятой, в собственной функции вывода их можно отсекать.

 Комментарии

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

В lua разделают однострочные комментарии (задаются через --):

-- вывод текста
message(text) -- можно ставить после функции

И многострочные (--[[ в первой строке и --]] в последней):

--[[
Подробное описание
функции или части алгоритма
--]]

В Notepad++ можно выделить нужные строки, правая клавиша мышки и выбрать «Закомментировать выделенное». Альтернатива: горячие клавиши CTRL + Shift + q.

Qlua: основы, часть 1
Правда Notepad делает это только через однострочные комментарии. Вернуть обратно – здесь же «Раскомментировать выделенное». Горячими клавишами раскомментировать: CTRL + Shift + k.

  

Типы данных

Как мы уже увидели, в qlua есть числовые (number) и текстовые (string) типы данных. Кроме этого есть еще 6:

nil неопределенный

boolean логический

table таблица

function функция

userdata пользовательские данные

thread поток

В lua используется динамическая типизация, т.е. переменная связывается с типом в момент присваивания значения, при этом до этого момента объявлять переменную не требуется. Напомню, что язык регистрочувствительный, это касается не только переменных, но и написания функций (в т.ч. собственных).

Допускается одновременное присваивание:

a, b, c = 5, 3, 7

И обмен значений:

b, c = c, b

 

nil это тип данных с одним значением nil (ничего/не существует). Любая переменная до того, как будет определена имеет по умолчанию именно это значение.

message(type(a)) -- выдаст nil, если ранее в коде не была определена переменная a.

Можно удалить ранее заданную переменную, присвоив ей nil.

a = 125
message(tostring(a))  -- выдаст 125
a = nil
message(tostring(a)) -- выдаст nil

boolean логический тип данных, имеет 2 значения false или true.

a = 125 -- задали переменную а как число
a = false – поменяли тип переменной

Строковые переменные (string) задаются одинарными кавычками, двойными, либо двойными скобками.

Т.е. следующим записи равнозначны:

text = "ООО 'Арка Текнолоджиз ' …"
text ='ООО "Арка Текнолоджиз " …'
text =[[ООО "Арка Текнолоджиз " …]]
text =[['ООО 'Арка Текнолоджиз ' …]]

message(text) выдаст текст с соответствующими кавычками.

 

В случае двойных квадратных скобок допускается перенос строк:

text =[[ первая строка
вторая строка ]]

Аналогичные переносы строк допускаются и при использовании кавычек, если перед переносом строки ставить \. Однако во всех случаях (как с двойными квадратными скобками, так и с кавычками) message выведет текст одной строкой, объединяя строки пробелами.


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

mytable = {10, 20, 30}

Либо сперва обозначить, что будет создана таблица (приравнять переменную пустому массиву {} ), а после уже определять её элементы:

mytable = {}
mytable[1] = 10
mytable[2] = 20
mytable[3] = 30

Двумерный массив определяется следующим образом:

mytable = {{}}
mytable[1][1] = 10
mytable[1][2] = 15
mytable[2][1] = 20
mytable[2][2] = 25

Либо сразу:

mytable = {{10, 15}, {20, 25}}

Аналогично задаются 3х мерные и большего порядка массивы.

Как мы видим, индексация в таблице начинается с 1. Причем в lua можно индексировать не только числами, но и строками или любым другим значением языка, кроме nil:

mytable = {}
mytable["SBER"] = {5, 15}
mytable["GAZP"] = {2, 13}
message(tostring(mytable["SBER"][2])) -- выдаст 15

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

Функция type() – возвращает строку-тип. Бывает полезной, чтобы лишний раз проверить какого типа переменная (если её не выводит message, например).

Попробуйте запустить код:

a = 125
message(type(a))  
a = nil
message(type(a))   
a = true
message(type(a))
a = {}
message(type(a))

Если всё сделано правильно, то выйдут в таблице сообщений 4 соответствующих типа данных.

  

Операции с числами.

Математические операции с числами стандартные:

Сумма:
c = a + b

Разность:
c = a — b

Деление:
c = a / b

Умножение:
c = a * b

Возведение в степень:
c = a ^ b

Остаток от деления:
c = a%b

Один из вариантов вывода чисел с округлением, это отминусовывание остатка от деления:


Например:

abc = 22 / 7
message("Число: "..abc)
message("Дробная часть: "..abc%1)
message("Округление до целого: "..(abc-abc%1))
message("Округление до десятых: "..(abc-abc%0.1))
message("Округление до сотых: "..(abc-abc%0.01))

Также в язык заложено большое количество математических функции, которые вызываются через math. Среди них есть в т.ч.:

 

math.abs              модуль числа
math.acos            арккосинус
math.asin             арксинус
math.atan            арктангенс
math.ceil              округление «вверх»
math.cos              косинус
math.deg              перевод угла из радиан в градус
math.exp              экспонента
math.floor            округление «вниз»
math.fmod           остаток от деления
math.log              натуральный логарифм
math.log10          десятичный логарифм
math.max            максимум
math.min             минимум
math.modf           вернёт целую и дробную часть числа
math.pi                число pi
math.rad              перевод градусы в радианы
math.random       генерация случайного числа
math.sin               синус
math.sqrt             квадратный корень
math.tan              тангенс

Для написания торговых скриптов этого с избытком хватит.

Например, число по модулю:
abc = math.abs(-50)

Максимум из 3 чисел получим через:
maxnum = math.max(10, 15, 20)

Случайное число в диапазоне от -10 до 10:
randnum = math.random(-10, 10)

Целую и дробную часть разделим через:
a, b = math.modf(22/7)


Операции со строками

Кроме конкатенации язык содержит разные полезные функции работы со строками, среди которых: 

string.byte         возвращает числовые коды символов в строке по индексу  
string.char          возвращает строку символов по их числовым кодам
string.find           поиск подстроки в строке, вернет индекс позиции или nil
string.format     форматирование строки с помощью опций
string.gsub         заменяет в исходной строке одну подстроку на другую
string.len            длина строки
string.lower       возвращает строку в нижнем регистре
string.rep            создает строку из копий подстроки
string.reverse   делает реверс строки
string.sub           делает подстроку из строки
string.upper      возвращает строку в верхнем регистре

Примеры:
a = string.byte(«abc», 1) – получаем 97

a = string.char(97, 98, 99) — получим abc

a = string.find(«abcdefg», «cd») — получим 3: cd нашли на 3й позиции

a = string.gsub(«abcdefg», «cd», «om») — замена cd на om abomefg

a = string.reverse(«abcdefg») — получим реверс: gfedcba

a = string.rep("*", 10) — строка из 10 звездочек

a = string.sub(«abcdefg», 3, 5) – подстрока с 3 по 5 индекс, получим: cde


Операции c
 таблицами (массивами).

insert добавляет элемент в таблицу.

Можно сделать с добавлением в конец массива:

mytable = {1, 2, 3, 4, 5}
table.insert(mytable, 6)                -- теперь mytable = {1, 2, 3, 4, 5, 6}

Либо вставить элемент по индексу, сдвигая оставшиеся элементы массива:

table.insert(mytable, 0, 1)           -- теперь  mytable = {0, 1, 2, 3, 4, 5, 6}


remove
удаляет из таблицы элемент:

Удаляет из таблицы элемент по индексу 3 и сдвигает оставшиеся элементы.

table.remove(mytable, 3)            -- теперь mytable = {0, 1, 3, 4, 5, 6}

concat осуществляет конкатенацию элементов массива в одну строку

table.concat(mytable)                  -- вернёт строку “013456”

# – позволяет получить длину строки или таблицы

#mytable                                      -- вернет 6

  

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

В языке реализована конструкция if в следующих вариантах:

 

1) if … then … end

if (условие) then
       -- часть кода, который выполняется, если условие верно
end

В случае, если необходимо сделать всего одну операцию, то удобнее запись разместить в строку:
If (условие) then … end

Например:

if a > b then b = a end

2) if … then … else … end

if (условие) then
        -- часть кода, который выполняется, если условие верно
else
        -- часть кода, который выполняется, если условие неверно
end

Пример:

if a > b then
      b = a
else
      b = 100
end


3) if … then … elseif … then … (elseif … then …)
x n… else … end

В этом случае можно расписывать разные варианты условий, в финале после указав else, если ни одно не выполнилось.

 

if a==100 then

-- часть кода, если а=100

elseif a==0 then

-- часть кода, если а=0

elseif a>100 then

-- часть кода, если а>100

else

-- часть кода, при других значения а

end

Сравнение происходит с помощью:

Равно                       == (на первых порах новички часто пропускают второй знак равенства);

Не равно                  ~=

Меньше                    <

Больше                     >

Меньше или равно    <=

Больше или равно     >=

 

Логические операции осуществляются с помощью and, or и not.

if a == 5 and b == 10 then
    -- код, если выполняются оба условия
end

if a == 5 or b == 10 then
     -- код, если выполняется хотя бы одно из условий
end

if not a then
    -- если а не существует (=nil)
end


Полезная проверка – если переменная существует:

if a then  -- равносильно записи if a~=nil then
   -- код, если a не nil
end


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

Условные выражения считают false и nil ложными, а все остальное – истинными.
Обратите внимание: qlua считает истинными как ноль, так и пустую строку в условных проверках.

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


Упражнение для закрепления (для желающих):

1. Сгенерируйте 2 случайных числа в пределах от 1 до 8 каждое.
2. Запишите результаты в таблицу.
3. Выведите в терминале строку: «Полученные координаты: (x, y)», — где x и y полученные случайные числа.
4*. Считая, что вы получили координаты поля на шахматной доске (x по горизонтали, y по вертикали, (1,1) черное поле) напишите алгоритм расчета цвета полученного поля. Выведите сообщение в терминал с полученным цветом поля (“черное”/”белое”).

Оглавление:

Qlua: настраиваем торговый терминал и редактор кода
Qlua: введение

Телеграмканала нет, ютубканала нет, роботов не продаю.
Теги: qlua для начинающих, кружок авиамоделизма.

★35
15 комментариев
Жуть.
Спасибо!
Здравствуйте!

Пробую создать массив по Вашему примеру:
mytable = {{}}
mytable[1][1] = 10
mytable[1][2] = 15
mytable[2][1] = 20
mytable[2][2] = 25

Квик выдает сообщение (строка 4: attempt to index a nil value (field 'integer index'))
Запускаю в Lua 5.4.1 (Quik 10.3.0.91)

Другие конструкции, которые Вы описали, все работают, подскажите что с этой не так?
avatar
User24, спасибо за вопрос!

Да, мой косяк:
mytable = {{},{}}

Также через цикл можно создать необходимую размерность:

mytable = {}
for i = 1, 2 do — двумерный массив
  mytable[i] = {}
end

И далее уже определять:

mytable[1][1] = 10
mytable[1][2] = 15
mytable[2][1] = 20
mytable[2][2] = 25


Либо сразу:
mytable = {{10, 15}, {20, 25}}
avatar
Спасибо, за информацию!
avatar
Полезно вспомнить основы
avatar

Спасибо за материал! Моё нубское решение упражнения:

x = math.random(1,8)
y = math.random(1,8)
mytable = {x, y}
message («Polucheny koordinaty:»..table.concat(mytable))
c = (x+y)/2
if (c%1==0) then
message («Black»)
else
message («White»)
end

avatar
Благодарю за Ваш труд! Мне лично, Вы очень помогли!
В описании операции insert в добавлении элемента по индексу есть опечатка — сперва должен идти номер элемента (в примере «1»), затем его значение (в примере «0»), иначе: " bad argument #2 to 'insert' (position out of bounds)". 
Евгений Гаврилов, спасибо! Да, вы правы.
Верная запись: 
table.insert(mytable, 1, 0)   
avatar

спасибо за материал

x = math.random(1,8)
y = math.random(1,8)
message(tostring(«x=»..x..«y=»..y))
mytable={«x»,«y»}
message(table.concat(mytable))

выдает две строки:
x=8y=4
xy

как его заставить выдавать значения xy, а не название переменных?

avatar
Не все функции работы со строками, числами и таблицами lua реализованы в qlua. concat, насколько я понимаю — это конкатенация, поэтому он просто объединяет их как строки.

Для вашего варианта можно использовать string.format, где в %s будет размещена соответствующая переменная:
message(string.format("%s * %s = %s", x, y, x*y))
avatar
вот эта конструкция очень странно работает для чисел с плавающей точкой, вроде ED, с шагом цены 0.0001
abc = 22 / 7
message("Число: "..abc)
message("Дробная часть: "..abc%1)
message("Округление до целого: "..(abc-abc%1))
message("Округление до десятых: "..(abc-abc%0.1))
message("Округление до сотых: "..(abc-abc%0.01))
в итоге конструкция вида price = price — price % price_step 
выдает совсем не то что ожидаешь(
это особенность языка или работы с числами с плавающей точкой?
avatar
mifologist, попробуйте либо поставить дополнительные нули: 

abc-abc%0.0001

либо сделать вывод через message(string.format("%.4f", abc))
avatar
alfacentavra, да, хороший вариант для шага цены меньше 1. спасибо)
avatar

теги блога alfacentavra

....все тэги



UPDONW
Новый дизайн