Перейти к содержимому
  • 0
Arsean

Не меняется значение переменной

Вопрос

Вообщем, немного разузнал про OpenSecurity и загорелся идеей сделать ворота с идентификатором хозяев дома. С самого начала всё пошло не очень, нету русскоязычных гайдов, сам язык я знаю не очень, раньше только с роботом работал. Но спустя дня 3 я во всём разобрался и застрял на довольно глупом месте. 

Цитата

entity = require("component").os_entdetector
alarm = require("component").os_alarm
door = require("component").os_rolldoorcontroller
local e = entity.scanPlayers(3)
local whitelist = {{}}

alarm.setAlarm("klaxon2")
alarm.setRange(15)
whitelist[1] = "Arsean"
whitelist[2] = "sherlock2202"

function open()
    door.open()
    os.sleep(5)
    door.close()
    detect()
end

function detect()
        if type(q[1]) == type(nil) then
            print("Нету")
            os.sleep(2)
            q = e
            detect()
         else
            print("Есть")
            q = e
            check()
        end
 end
function check()
    x = e[1].name
    if x == "Arsean" then
        open()
    else
        alarm.activate()
        os.sleep(5)
        alarm.deactivate()
    end
    detect()
end
q = e
detect()

 

Я перепробовал множество идей, и пришел к полному абсурду(это то, что вы сейчас читаете). Суть системы заключается в том, что она раз в 2 секунды проверяет, есть-ли кто-нибудь из игроков рядом, если есть то проверяет кто, если хозяин, то открывает дверь, если нет, то включает сирену(что бы знать, когда кто-то трётся и твоих дверей).

 

Ошибка заключается в том, что он присваивает q значение и не меняет его. Если запускаешь программу когда рядом стоишь, он всегда будет видеть что ты рядом, если запускаешь когда он тебя не видит, то он не изменит своего мнения.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Рекомендуемые сообщения

Чтобы решить проблему, надо оформить код.

entity = require("component").os_entdetector
alarm = require("component").os_alarm
door = require("component").os_rolldoorcontroller
local e = entity.scanPlayers(3)
local whitelist = {{}}

alarm.setAlarm("klaxon2")
alarm.setRange(15)
whitelist[1] = "Arsean"
whitelist[2] = "sherlock2202"

function open()
  door.open()
  os.sleep(5)
  door.close()
  detect()
end

function detect()
  if type(q[1]) == type(nil) then
    print("Нету")
    os.sleep(2)
    q = e
    detect()
  else
    print("Есть")
    q = e
    check()
  end
end
function check()
  x = e[1].name
  if x == "Arsean" then
    open()
  else
    alarm.activate()
    os.sleep(5)
    alarm.deactivate()
  end
  detect()
end
q = e
detect()

Тяк, сначала починим обозначенную неполадку. Функция detect, согласно названию, должна при каждом вызове пересканировать игроков. Однако сканирование игроков это происходит в самом начале программы (4 строка) и один раз. Гм. Ещё раз, при каждом вызове функции надо выполнять определённый код. При каждом вызове... а почему бы не поместить сканирование внутрь функции? Разве это не тем и занимается?

entity = require("component").os_entdetector
alarm = require("component").os_alarm
door = require("component").os_rolldoorcontroller
local e = entity.scanPlayers(3)
local whitelist = {{}}

alarm.setAlarm("klaxon2")
alarm.setRange(15)
whitelist[1] = "Arsean"
whitelist[2] = "sherlock2202"

function open()
  door.open()
  os.sleep(5)
  door.close()
  detect()
end

function detect()
  e = entity.scanPlayers(3)
  q = e
  if type(q[1]) == type(nil) then
    print("Нету")
    os.sleep(2)
    q = e
    detect()
  else
    print("Есть")
    q = e
    check()
  end
end
function check()
  x = e[1].name
  if x == "Arsean" then
    open()
  else
    alarm.activate()
    os.sleep(5)
    alarm.deactivate()
  end
  detect()
end
q = e
detect()

Ну да, почти. Пришлось только ещё q = e перетащить туда же. Но теперь должно работать.

...

Должно ли?

 

Исправляем другие проблемы, которые здесь заютились.

Проблема 1. Есть переменные q и e, которые во всех местах устанавливаются одинаковыми, при этом непонятно, чем они отличаются.

Объединим их в одну переменную. Заодно назовём её по-человечески, а не для машины, то есть понятно:

entity = require("component").os_entdetector
alarm = require("component").os_alarm
door = require("component").os_rolldoorcontroller
local scan = entity.scanPlayers(3)
local whitelist = {{}}

alarm.setAlarm("klaxon2")
alarm.setRange(15)
whitelist[1] = "Arsean"
whitelist[2] = "sherlock2202"

function open()
  door.open()
  os.sleep(5)
  door.close()
  detect()
end

function detect()
  scan = entity.scanPlayers(3)
  if type(scan[1]) == type(nil) then
    print("Нету")
    os.sleep(2)
    detect()
  else
    print("Есть")
    check()
  end
end
function check()
  x = scan[1].name
  if x == "Arsean" then
    open()
  else
    alarm.activate()
    os.sleep(5)
    alarm.deactivate()
  end
  detect()
end
detect()

 

Проблема 2. Куча переменных не локальны.

Допустим такую ситуацию: в коде переименовали переменную, а в одном месте имя сменить забыли. Если эта переменная была локальной, то программа пропишет ошибку, стопнется, и проблему легко локализовать и починить. Если глобальной, то она никуда не денется до рестарта компьютера. Каково этого — поменял переменную, а в одном месте как будто одно и то же значение застряло? Чинить такие вещи — боль.

Как правило, все переменные делать надо локальными. У глобальных есть несколько применений, но без них обходиться можно всегда. И лучше это делать.

Чиним, короче:

local entity = require("component").os_entdetector
local alarm = require("component").os_alarm
local door = require("component").os_rolldoorcontroller
local scan = entity.scanPlayers(3)
local whitelist = {{}}

alarm.setAlarm("klaxon2")
alarm.setRange(15)
whitelist[1] = "Arsean"
whitelist[2] = "sherlock2202"

local function open()
  door.open()
  os.sleep(5)
  door.close()
  detect()
end

local function detect()
  scan = entity.scanPlayers(3)
  if type(scan[1]) == type(nil) then
    print("Нету")
    os.sleep(2)
    detect()
  else
    print("Есть")
    check()
  end
end
local function check()
  local x = scan[1].name
  if x == "Arsean" then
    open()
  else
    alarm.activate()
    os.sleep(5)
    alarm.deactivate()
  end
  detect()
end
detect()

 

Проблема 3. Переполнение стэка из-за рекурсии.

Во всех функциях в коде вызывается detect. В том числе внутри самой detect — когда функция саму себя зовёт, это зовут рекурсией.

 

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

Откуда тут рекурсия? Думаю, автор рассуждал так: после проверки на пробежчиков мы хотим снова отсканировать игроков. То есть перейти в detect. А как перейти в функцию? Вызовом же.

 

Проблема в том, что Lua — язык императивный по большей части. А ещё оптимизаций делает мало. Поэтому не получится в луа сколь угодно много раз вызывать функции рекурсивно. Упрёмся в лимит и словим ошибку.

 

Один способ починить — использовать хвостовую рекурсию, которую Lua оптимизировать умеет. Мы вместо этого воспользуемся циклами.

local entity = require("component").os_entdetector
local alarm = require("component").os_alarm
local door = require("component").os_rolldoorcontroller
local scan = entity.scanPlayers(3)
local whitelist = {{}}

alarm.setAlarm("klaxon2")
alarm.setRange(15)
whitelist[1] = "Arsean"
whitelist[2] = "sherlock2202"

local function open()
  door.open()
  os.sleep(5)
  door.close()
end

local function detect()
  while true do
    scan = entity.scanPlayers(3)
    if type(scan[1]) == type(nil) then
      print("Нету")
      os.sleep(2)
    else
      print("Есть")
      check()
    end
  end
end

local function check()
  local x = scan[1].name
  if x == "Arsean" then
    open()
  else
    alarm.activate()
    os.sleep(5)
    alarm.deactivate()
  end
end

detect()

Если в первой функции вызвать вторую, то Луа будет выполнять код второй функции. Когды мы дойдём до конца кода её, Луа вернётся в первую функцию. То есть Lua не забывает, кто вызвал любую функцию. Таким образом, open вернётся в check, а check — в detect.

 

В detect появился while true do ... end. Эта конструкция называется бесконечным циклом. Цикл — повторение одного и того же кода. Бесконечный — нет условия, при котором программа покинет цикл. (Формально есть ^[C, то есть цикл покинуть можно, но оставим это в стороне.)

 

Проблема 4. Нелокальность переменных.

Это не повторение проблемы #2, хотя на определённом уровне абстракции всё начинает казаться молотками и гвоздями они очень похожи.

Переменная scan общая для всех функций. По сути, это тоже "глобальная" переменная, только живёт она не дольше, чем программа (статическая она, наверное, лучше сказать). Если нет веских причин, лучше всё-таки такие переменные передавать явно. То есть аргументом функций.

local entity = require("component").os_entdetector
local alarm = require("component").os_alarm
local door = require("component").os_rolldoorcontroller
local whitelist = {{}}

alarm.setAlarm("klaxon2")
alarm.setRange(15)
whitelist[1] = "Arsean"
whitelist[2] = "sherlock2202"

local function open()
  door.open()
  os.sleep(5)
  door.close()
end

local function detect()
  while true do
    local scan = entity.scanPlayers(3)

    if type(scan[1]) == type(nil) then
      print("Нету")
      os.sleep(2)
    else
      print("Есть")
      check(scan)
    end
  end
end

local function check(scan)
  local x = scan[1].name
  if x == "Arsean" then
    open()
  else
    alarm.activate()
    os.sleep(5)
    alarm.deactivate()
  end
end

detect()

Теперь scan определяется в detect и передаётся в check аргументом.

 

Проблема 5. Программа страшно боится одиночества.

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

local x = scan[1].name

Я утверждаю, что тут ошибка. Разберём строку.

  1. scan — это таблица с игроками. Она хранит внутри себя ещё таблицы. В последних инфа о конкретном игроке.
  2. scan[1] — это тогда таблица с инфой о первом игроке. А если вокруг радара пустыня, случайных прохожих нет, а последний местный подох под палящим пустынным солнцем где-то в лаве? scan будет пустой таблицей, и scan[1] будет nil.
  3. scan[1].name — эта штука безусловно полагает, что scan[1] можно индексировать, то есть это таблица (или что-то вроде неё). Нет, не scan — то, что это таблица, мы уже знаем безусловно. scan[1] — первый элемент таблицы scan. Выше мы определили, что этот элемент — или ещё одна таблица, или nil.

Таблицу индексировать можно. Если проиндексировать nil, программа завершится с ошибкой. Напомню, nil у нас бывает только тогда, когда игроков вокруг нет. Следовательно, на необитаемом острове программа упадёт.

 

Чтобы починить, подумаем, что надо делать на этом необитаемом острове. Визжать сиреной? Думаю, вряд ли. Лучше просто промолчать и ничего не делать. Добавим проверку.

local entity = require("component").os_entdetector
local alarm = require("component").os_alarm
local door = require("component").os_rolldoorcontroller
local whitelist = {{}}

alarm.setAlarm("klaxon2")
alarm.setRange(15)
whitelist[1] = "Arsean"
whitelist[2] = "sherlock2202"

local function open()
  door.open()
  os.sleep(5)
  door.close()
end

local function detect()
  while true do
    local scan = entity.scanPlayers(3)

    if type(scan[1]) == type(nil) then
      print("Нету")
      os.sleep(2)
    else
      print("Есть")
      check(scan)
    end
  end
end

local function check(scan)
  if scan[1] then
    local name = scan[1].name

    if name == "Arsean" then
      open()
    else
      alarm.activate()
      os.sleep(5)
      alarm.deactivate()
    end
  end
end

detect()

Не удержался, переменовал x и name, чтобы не потеряться.

 

Проблема 6...

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

В этом посте я попытался описать только "сложные" проблемы, то есть для определениях которых нужен какой-нибудь опыт программирования.

Проблема 3, например, означает, что программа крашилась бы через час после включения. Стабильно. Со странной ошибкой.

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

Изменено пользователем Fingercomp
Запятая лишняя затесалась
  • Нравится 5
  • Спасибо 1
  • В шоке 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Cпасибо за значительное улучшение кода!
Но теперь при активации функции check(scan) он не может индексировать значение nil(скриншот приложил)
Т.е. когда я подхожу к сканеру, он запускает функцию и ложится с ошибкой

 

Скрытый текст

image.thumb.png.85c7bd1e449ab33bb28b71cf36cdcb89.png

 

Изменено пользователем Alex
Установка спойлера

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

попробуй в коде поменять местами функции  local function check(scan)  и local function detect()

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Не знаю как, но это помогло, спасибо )

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

ок :)

код выполняется построчно, а так как функции теперь локальные а не глобальные, у верхней функции нет доступа к нижней

также можно завернуть весь код в одну функцию, так будет проще ориентироваться в коде,

функции обычно выносят при частом обращении из разных мест в коде, чтоб не повторять её по 100 раз

Скрытый текст

local entity = require("component").os_entdetector
local alarm = require("component").os_alarm
local door = require("component").os_rolldoorcontroller
local whitelist = {{}}

alarm.setAlarm("klaxon2")
alarm.setRange(15)
whitelist[1] = "Arsean"
whitelist[2] = "sherlock2202"

local function detect()
  while true do
    local scan = entity.scanPlayers(3)
    if type(scan[1]) == type(nil) then
      print("Нету")
      os.sleep(2)
    else
      print("Есть")
      if scan[1] then
        local name = scan[1].name
        if name == "Arsean" then
          door.open()
          os.sleep(5)
          door.close()
        else
          alarm.activate()
          os.sleep(5)
          alarm.deactivate()
        end
      end
    end
  end
end

detect()

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

осталось whitelist допилить

что то типа этого 

Скрытый текст

local nicknames = {"Arsean","sherlock2202"}

local name = scan[1].name
for i = 1, #nicknames do
  if name == nicknames[i] then
    print("ok")
    break
  end
end

 

 

Изменено пользователем serafim

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Вайтлист, сразу сделал, но спасибо за идею )

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...