Циклы for while repeat и оператор break в LUA

Циклы for while repeat в lua

Циклы for while repeat в lua

Приступаем к важной теме «циклы». Для начала определимся с тем что такое циклы, для чего они нужны и в каких случаях их применяют.

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

Представьте следующую ситуацию. Вам необходимо найти на графике бар у которого цена закрытия равна 50. Предположим что график состоит из 3000 баров. Каким образом найти бар у которого цена закрытия равна 50? Необходимо сделать следующие. Нужно взять 1 бар, запросите его цену закрытия и сравнить с ценой 50, далее необходимо взять 2 бар, запросить его цену закрытия и сравнить с ценой 50, потом взять 3 бар, запросить его цену закрытия и сравнить с ценой 50 и так далее, пока не просмотрим все 3000 баров. Код программы будет выглядеть примерно так:

Если цена закрытия 1 бара = 50 тогда оповестить пользователя
Если цена закрытия 2 бара = 50 тогда оповестить пользователя
Если цена закрытия 3 бара = 50 тогда оповестить пользователя

Если цена закрытия 3000 бара = 50 тогда оповестить пользователя

Как видите такая простая задача, как поиск бара с определенной ценой закрытия, будет занимать как минимум 3000 строк. Есть ли у вас желание писать такой громадный код? Надеюсь что нет.

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

Начало цикла. n изменять от 1 до 3000
Если цена закрытия n бара = 50 тогда оповестить пользователя
Конец цикла

Теперь наш код состоит из 3 строк. Строка где будет выполняться сравнение цены закрытия с ценой 50 будет выполнено 3000 раз, но номер бара будет меняться от 1 до 3000. Это теоретическое представление цикла показывает, что обходиться без цикла практически невозможно.

Переходим к практике. Необходимо разобраться какие циклы бывают в lua, как их необходимо описывать, а также как они работают.

Любой цикл состоит из 2 частей. Первая часть это объявление цикла. Вторая часть это тело цикла.

Первый вариант цикла for.

Простой вариант цикла for позволяет выполнить перечисление переменной в указанном диапазоне, как раз то что мы делали немного выше в теоретическом коде.

function main()
	for n = 1, 3000 do
		if (n == 50) then
			message("Нашли нужный бар " .. n);
		end
	end
end

В этом примере объявление цикла описано во 2 строке, а тело цикла это строки под номерами 3, 4 и 5.

Для того чтобы не запрашивать цену закрытия бара в примере выше я просто сравниваю номер бара с 50. Переменная n меняется от 1 до 3000, включая и значение 3000 тоже. Как только n становится равным 50 выполняется условие и появляется сообщение, далее цикл продолжается.

Чуть более расширенную версию цикла for можно написать следующим образом

function main()
	for n = 1, 3000, 1 do
		if (n == 50) then
			message("Нашли нужный бар " .. n);
		end
	end
end

Как видите практически ничего не изменилось, единственное в объявлении цикла появилось еще одно число 1 после 3000. Этим параметром мы можем задавать шаг цикла. Так как в примере выше шаг установлен 1, то переменная n меняется как 1 2 3 4 5 и так далее. Если мы напишем шаг 2, то переменная n будет меняться как 1 3 5 7 9 и так далее, в этом случае переменная n не попадает в 50, а значит и сообщение не появится.

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

function main()
	for n = 3000, 1, -1 do
		if (n == 50) then
			message("Нашли нужный бар " .. n);
		end
	end
end

В этом примере переменная n меняется от 3000 до 1 в обратном порядке. Как только переменная n будет равняться 50, условие выполняется, и появится сообщение.

Второй вариант цикла for.

Второй вариант цикла for можно применять для перебора значений в массиве (таблице).

function main()
	t = {};
	t[1] = 10;
	t[2] = 20;
	t[3] = 30;
	t[4] = 40;
	t[5] = 50;
	t[10] = 100;
	t[120] = 1200;
	for n, val in ipairs(t) do
		message("Номер " .. n .. " | Значение " .. val);
	end
end

В примере выше у нас имеется таблица t, в которую мы записали несколько значений. В поля таблицы под номерами 1 2 3 4 5 записаны значения соответственно 10 20 30 40 50. Далее в поле под номером 10 записано значение 100, а в поле под номером 120 записано значение 1200.

После заполнения таблицы мы объявляем цикл, где переменная n будет перебирать индексы нашей таблицы, переменная val будет принимать значение поля указанного индекса, после in мы пишем в какой таблице перебирать индексы.

Если вы запустите этот код на выполнение, то возможно немного удивитесь, так как увидите всего 5 сообщений. Вы увидите номера индексов от 1 до 5 и их значения. Индексы под номерами 10 и 120 обработаны не будут. Дело в том что этот вариант цикла for перебирает лишь только идущие подряд индексы без пропусков, как только встречаются отсутствующий индекс, а в нашем примере это индекс номер 6, цикл прекращается.

Также этот вариант цикла не подходит для таблиц в которых индексы не являются числами например такой вариант:

function main()
	t = {};
	t["red"] = 10;
	t["green"] = 20;
	t["blue"] = 30;
	t["white"] = 40;
	t["black"] = 50;
	t["cyan"] = 100;
	t["yellow"] = 1200;
	for n, val in ipairs(t) do
		message("Номер " .. n .. " | Значение " .. val);
	end
end

Запустив такой код вы не увидите ни одного сообщения.

Третий вариант цикла for.

Рассмотрим еще один вариант как можно описать цикл for. Этот вариант как и предыдущий предназначен для перебора индексов в массивах (таблицах).

function main()
	t = {};
	t["red"] = 10;
	t["green"] = 20;
	t["blue"] = 30;
	t["white"] = 40;
	t["black"] = 50;
	t["cyan"] = 100;
	t["yellow"] = 1200;
	for key, val in pairs(t) do
		message("Ключ " .. key .. " | Значение " .. val);
	end
end

Посмотрите внимательно на этот и предыдущий коды, как говорится найдите отличие. На первый взгляд они абсолютно одинаковые но отличие всё-таки есть, ipairs(t) заменили на pairs(t). Будьте внимательны при написании, изменение всего в одной букве влияет на работу кода. Также для удобства имя переменной n заменили на key, это не является различием так как имена переменных могут быть абсолютно любыми.

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

Цикл while в lua.

Ещё один вариант с помощью которого можно объявить цикл в языке lua является цикл while. Этот цикл не перебирает какие-то значения переменных как это делал цикл for, а выполняется до тех пор пока выполняется условие.

function main()
	local i = 1;
	while(i < 5) do
		message("Значение i = " .. i);
		i = i + 1;
	end
end

В третьей строке написано пока условие указанное в скобках выполняется, выполнять тело цикла.

Сначала переменная i у нас равна 1, так как 1 меньше 5 значит условие выполняется, следовательно перейти к выполнению тела цикла. В теле цикла описано показать сообщение «значение переменной i «, а так же к переменной i прибавить 1. Тело цикла заканчивается и переходим снова к проверке условия. Теперь переменные i равна 2, что так же меньше 5, значит условие выполняется, повторяем тело цикла. Когда переменная i будет равна 5, то условие уже не выполнится, а значит цикл прекратится. Следовательно последнее сообщение которое вы увидите запустив данный код будет «Значение i = 4». Условие может быть абсолютно любым, а не только сравнение чисел, главное если условие выполняется, то выполняется тело цикла, иначе цикл прекращается.

Цикл repeat — until в lua.

Последний вариант цикла в lua, который мы рассмотрим, очень сильно похож на предыдущий вариант while, но работает немного по-другому. Этот вариант также предусматривает проверку условия, но наоборот, как только условие выполнится цикл прервется.

function main()
	local i = 1;
	repeat
		message("Значение i = " .. i);
		i = i + 1;
	until i > 5
end

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

Первое сообщение которое мы получим «Значение i = 1», потом к i прибавим 1, далее будет выполнена проверка 2 больше 5 ? — нет, значит цикл продолжается. Когда мы получим сообщение «Значение i = 4», после к i прибавим 1 и переменная i будет равна 5, производится проверка 5 больше 5 ? — нет, значит цикл продолжает работать. Появляется сообщение «Значение i = 5», прибавляем 1 и переменная i уже равна 6, при проверке условия 6 больше 5 ? — да, цикл прекращается.

Вот такие варианты циклов можно использовать при программирование на языке lua.

Оператор break

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

function main()
	local i = 1;
	while (true) do
		message("Значение i = " .. i);
		if (i == 5) then
			break;
		end
		i = i + 1;
	end
end

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

В этом примере использована дополнительно проверка на условие переменной i, когда переменная i будет равна 5 условие выполняется, будет вызван оператор break, который прервет цикл. Таким образом данный цикл покажет 5 сообщений и цикл прервется.

На этом мы заканчиваем тему с циклами, надеюсь всё понятно, по традиции все вопросы жду в комментариях.

Постоянная ссылка на это сообщение: https://k-pavel.ru/cikly-for-while-repeat-i-operator-break-v-lua/

avatar
6 Цепочка комментария
40 Ответы по цепочке
2 Последователи
 
Популярнейший комментарий
Цепочка актуального комментария
5 Авторы комментариев
Павел КашинскийРоманАлександрИринаДмитрий Авторы недавних комментариев

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.

  Subscribe  
Новые Старые Популярные
Подписаться на
Роман
Гость
Роман

Здравствуйте, Павел! Помогите разобраться! Задача: видеть сегодня вчерашний ХАЙ и ЛОУ на М5 в виде прямых линий от начала истории. Немного изменил код индикатора, в результате в назначенное время всё рассчитывается, сохраняется и отображается, но при смене инструмента в другое время значения не рассчитываются и линии не отображаются до наступления указанного времени. Можете что-то посоветовать?

Settings= {
Name = "*HIGH-LOW_YESTERDAY",
period = 162,
line =
{
	{
		Name = "HIGH",
		Color = RGB(255, 255, 255),
		Type = TYPE_LINE,
		Width = 3,

	},
	{
		Name = "LOW",
		Color = RGB(255, 255, 255),
		Type = TYPE_LINE,
		Width = 3,

	},
}

}

function Init()
return #Settings.line
end

		local inter = Settings.period
		local t_table = {}
		local HIGH
		local LOW


function OnCalculate(indx)

	local tt = T(indx)

  if (indx == Size()) then

	if tt.hour == 23
	and	tt.min == 45
	then
		t_table = tt

		HIGH = H(indx)
		LOW = L(indx)

					for i = 0, inter-1 do
						if H(indx-i) > HIGH then
						HIGH = H(indx-i);
						end
						if L(indx-i) < LOW then
						LOW = L(indx-i);
						end
					end
						for i = 1, indx do
						SetValue(i, 1, HIGH);
						SetValue(i, 2, LOW);
						end

	end

   end

	return HIGH, LOW
end

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

Роман
Гость
Роман

Здравствуйте, Павел! Подскажите, если Вам не трудно, где у меня ошибка. хочу найти хай за период, а индикатор перебирает все данные источника и вместо одной линии соединяет максимумы.

function OnCalculate(indx)
if indx = high then
high = price
end
end
message(«Значение high = » ..high);
return high
end
end

Роман
Гость
Роман

Здравствуйте Павел! Огромное Вам спасибо! На самом деле у меня была идея сделать индикатор High-Low дня с расчётом по времени (например, в 10.00 началась торговая сессия, на 12.00 образовались какие-то локальные ХАЙ и ЛОУ, в 11.59.59 производим расчёт ХАЯ и ЛОУ через дополнительное условие (T(indx)) по времени (прямая горизонтальная линия через всю историю или же от начала торговой сессии). Прошло ещё время, например в 15.00 (в 14.59.59 производим перерасчёт на указанное время (то есть положение прямой горизонтальной линии меняется ). В промежутке от 12.00 до 15.00 ХАЙ и ЛОУ на 12.00 сохраняются, независимо от текущей цены в этот период времени. И таким образом хотелось сделать несколько временных точек в течение торговой сессии. Кроме того, при изменении рассчитанных значений, предыдущее значение удаляется (другими словами, на графике инструмента всегда (после 12.00 только две прямых горизонтальных линии (ХАЙ и ЛОУ). И ещё один вопрос, Павел, как в параметрах Settings создать отключаемый параметр индикатора (отображается в свойствах индикатора в терминале в виде галки в окошке?

Роман
Гость
Роман

Добрый день, Павел! Огромное Вам спасибо! Вы сберегли мои глаза и нервы. Но вот ещё вопрос. Что надо изменить, чтобы исключить историю расчётов, а отображать один только хай за указанный период (прямая горизонтальная линия от начала истории и до текущего момента)?

Роман
Гость
Роман

Павел! А можно ли ограничить OnCalculate() определённым диапазоном (от последнего бара на определённое кол-во баров назад)? Или же она всё равно будет проходить от начала истории?

Роман
Гость
Роман

А если так?

function OnCalculate(indx)
if indx < indx-period then return nil

Как думаете?

Роман
Гость
Роман

Хотелось отфильтровать кол-во свечей

Роман
Гость
Роман

Или может быть как-то можно использовать Size() для указания диапазона ? или в данном случае Size() не сработает?

Роман
Гость
Роман

Спасибо! Попробую

Роман
Гость
Роман

Попробовал! Так вообще не выводит ничего
function OnCalculate(indx)

local S = Size()
if indx >= S-period then

for i = 0, period-1 do

local prc = 0;
local HIGH = 0;

for i = 0, period-1 do
prc = H(indx-i);

if prc > HIGH then
HIGH = prc;
end
end
end

return HIGH

else
end

end

хотя значение Size() есть!? Может условие не то? Хотя всё вроде правильно.

Роман
Гость
Роман

Добрый день, Павел! Вот может быть через таблицы (создать, занести значения и рассчитать) лучше? Но где-то у меня ошибка!?

local period = 9

function OnCalculate(indx)

local ht = {}
local prc = 0;
for i = 0, period-1 do
prc = H(indx-i);
ht[i] = prc
end

local HIGH = 0
for i = ht[1], #ht do

if ht[i] > HIGH then
HIGH = ht[i]
end
end
return HIGH
end

Роман
Гость
Роман

Или так, но тоже где-то напутал!

local period = 9

function OnCalculate(indx)

local ht = {}

for i = 0, period-1 do
table.insert(ht, {H_value = H(indx)})
end

local HIGH = 0
for i = ht[1], #ht do

if ht[i] > HIGH then
HIGH = ht[i]
end
end
return HIGH
end

Роман
Гость
Роман

Добрый день, Павел! Не смог сразу Вам ответить, так как днём приходится торговать, а ночами и по выходным учиться программированию. Должен Вам сказать, глядя на код индикатора, что это изумительно. Так лаконично и просто. А я в программировании, как выходец из союзной республики, который русский язык понимает,но говорить правильно не может. У меня, к сожалению, зачастую, действительно нет окончательного понимания взаимосвязи некоторых процессов внутри кода. А интерпретатор QLUA от создателей QUIK написан так, что не всё простому смертному понятно, не хватает примеров использования некоторых функций. вот и приходится собирать по сети примеры использования функций. Но открытых кодов мало. Обычно попадаются книжные примеры, которые зачастую сложно применить в работе с терминалом. А на самом деле всё так просто. Интересно первое условие: if (indx == Size()) — это, другими словами такое указание на источник, чтобы избежать лишних переменных? И вот интересно использование третьего параметра функции SetValue (в интерпретаторе QLUA нет примера использования данного третьего параметра). А если убрать цикл на SetValue: SetValue(index-1, 1, HIGH), или же i и здесь как управляющая переменная? И ещё вот вопрос: как сделать условия указания на сегодняшний или на вчерашний день без указания даты в условии?
И последний вопрос: как правильно вставлять код на сайте, а то он при копировании приобретает неприглядный вид?
Павел, я Вам очень благодарен за неоценимую помощь в создании индикатора. До знакомства с Вами я встречал только примеры с использованием идентификатора для получения максимумов и минимумов. А на самом деле всё оказалось так просто. Очень Вам признателен!

Александр
Гость
Александр

Здравствуйте. Не могу реализовать цикл с дополнительной проверкой. Цель дополнительной проверки, что бы исключить из таблицы всю строчку параметров (возможно несколько строчек), один из которых не соответствует условию. Есть мысль реализовать как функцию. Возможно есть еще какой-нибудь вариант?

local i = 1														
while StopRead == true and do								
	local a, b, c = FileRead:read("*n", "*n", "*n")
	if a == nil then StopRead = false						
	else														
		if b <184 then
		ATable[i] = a									
		BTable[i] = b
		CTable[i] = c
		i = i + 1												
		end
	end	    													
end
Александр
Гость
Александр

Я не совсем точно выразился. Необходимо что бы проверка не останавливалась, а пропускала значения => 184 и продолжалась до окончания таблицы (а==nil). Пробовал присваивать а, b,c =nil, но как я понимаю, в таблице в таком случае появляются пустые строчки? Это затрудняет работу с массивом

Александр
Гость
Александр

Здравствуйте. Благодарю Вас за ваше время и работу. Вы показали на что необходимо обратить внимание. Для того что бы получить нужный ответ, необходимо задать правильно вопрос. Пришлось немного подтянуть теоретическую часть и как результат появилось еще больше вопросов.
У меня есть просьба к Вам. Могли бы Вы сделать такой же обзор и осветить ряд вопросов:
1. Разобрать по строчно действия стандартных индикаторов Фрактал, МАСD или любого другого для Квика. Начиная от входных характеристик и функций и заканчивая способами от рисовки индикатора
2. Методы сортировки данных и действий над ними: включение строк или удаления переменной в многоуровневых таблицах. Сортировка уже отсортированного для выяснения индекса свечи. Методов очень много, хотелось бы узнать ваше мнение о наиболее эффективном алгоритме из вашего опыта.
3. Метод написания кода индикатора с различными временными периодами (нижестоящего или вышестоящего графика) в одном окне. Один индикатор разных инструментов (например, чтобы видеть в одном окне поведение АDХ различных инструментов)
Заранее прошу прощение, если подобное уже было освящено и в таком случае прошу Вас указать ссылку, для ознакомления. Еще раз спасибо за ваши статьи.

Ирина
Гость
Ирина

И ещё пара вопросов, если найдете время.
Какой цикл предпочтителен и почему (больше всего интересуют затраты памяти и время выполнения, конечно): while или repeat? Почему в функции main обычно используется while?
На просторах интернета вычитала о существовании функции foreach(), которая быстрее while. В руководстве Lua такого слова нет. Не знаете, что это?
Заранее спасибо за ответы и ещё раз за статьи!

Ирина
Гость
Ирина

Спасибо большое за ответ — очень полезен!

Ирина
Гость
Ирина

Павел, если знаете, подскажите, пожалуйста.
Для цикла вида

for n = a, b do тело цикла end

где a, b — заданные заранее переменные и a может оказаться больше b,

надо ли вводить дополнительную проверку на соответствие a<=b или цикл сам отвергнется?
Ну и "for n = b, a, -1 do тело цикла end" аналогичен?

Ирина
Гость
Ирина

Спасибо, Павел! Предельно понятно, как всегда. 🙂
Здорово это! При таком раскладе у меня циклов в 2 раза меньше получается. Нравится простота Lua!

P.S. (Вы не заметили, что во втором цикле, с шагом -1, я a и b местами поменяла — не учтено в объяснении — это я для читателей, чтоб не запутались).

Дмитрий
Гость
Дмитрий

Здравствуйте. Скажите можно в квик поставить реакцию на клавиатуру или мышь. Что бы если я по ошибки напишу бесконечный цикл (и понял это только во время работы) нажал на клавишу и цикл прервался.

Дмитрий
Гость
Дмитрий

Спасибо за ответ. Очень благодарен. Приму к сведению