000179. Избавляемся от "ёлочек"

Избавляемся от "ёлочек"|Привет! В этой статье я хотел бы поделится опытом написания скриптов. А точнее принципом написания кода таким способом, чтобы его легче было читать. Речь пойдёт о "ёлочках".|wmysterio|wmysterio||||Под этим термином обычно подразумевается конструкция типа "IF-THEN-ELSE-END". Сама по себе она лёгкая и часто используется. Проблемы начинаются тогда, когда такие конструкции являются вложенными в другие, такие же, конструкции. Иногда таких конструкций становится настолько много, что читать такой код очень тяжело как опытным скриптерам, так и самому автору.

Давайте рассмотрим простой пример для лучшего понимания ситуации, а дальше будут примеры более сложные. Есть такой код:

:LABEL
wait 0
if
056D: actor 0@ defined
then
02AB: set_actor 0@ immunities BP 1 FP 1 EP 1 CP 1 MP 1
jump @LABEL_2
else
0376: 0@ = create_random_actor_at -1576.88 55.26 8.57
end
jump @LABEL

:LABEL_2

Как же решать подобную проблему и сделать код легко читаемым? Первый принцип подхода таков: создаём (или используем) метку выхода с конструкции и пытаемся написать код таким способом, чтобы избавится от блока "ELSE".

У нас сейчас доступны две метки: "LABEL" и "LABEL_2". Мы можем использовать их чтобы делать простые прыжки. Сами условия пишем так, чтобы блок "ELSE" оказался не нужным.

Вариант решения №1:

Вариант решения №2:

13 строк кода мы сократили до 10 (8) строк и ещё избавились от цикла. Удивительно, но такой простой принцип лежит в основе "лёгкого" кода. Здесь важно не количество строк, f отсутствие отступов. Мы пишем код сверху вниз, а не в ширину. Давайте рассмотрим код посложнее, где эти "отступы" будут явно видны:

Давайте попытаемся избавится от всех блоков "ELSE":

Мы по прежнему сократили количество строк и код у нас никуда не едет вправо. Читая скрипт, мы точно знаем что происходит в каждом блоке, без поиска отдельной части "ELSE", которая находилась где-то в "подвале" кода. Рассмотрим пример ещё сложнее:

Мы видим, что у нас много разветвлений. При этом у нас в "ELSE" блоке проверки отличаются только числом, что сравнивается, и действиями. Как поступить в этом случае? Решение ситуации заключается в "разбиении" сложной конструкции на мелкие фрагменты:

Мы опять сократили код и избавились от блоков "ELSE". Первое условие в коде позволяет не выполнять код ниже (ключевое слово "continue"), если оно равно "false". Это позволило нам избавится от лишних разветвлений в блоке с условием "0@ == 0". В блоке кода с условием "0@ == 3" действия блока "ELSE" фактически являлись изначальными данными, поэтому их легче написать после опкода 077E.

Наличие "continue" и изначальных данных приводит нас к ещё одному принципу по избеганию "ёлочки". Принцип таков: сначала пишем условия, которые не позволяют выполнять всю логику, если оно равно "false".

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

Тем не менее, не всегда получается избавится от "ELSE", так как это нарушит работу алгоритма или в разветвлении совершаются принципиально разные действия. В этом случае, нужно соблюдать те же принципы, но в каждом из разветвлений. Рассмотрим такой скрипт:

Давайте попробуем упростить скрипт по этапам:

    1. Смотрим на все блоки "ELSE", которые вложены максимально "глубоко" в другие "ELSE" или "THEN". То есть начинаем с конца.

У нас опкоды 04BA и 02E2 выполняются если все родительские блоки в условиях возвращают "false". Поэтому мы можем все вложенные условия записать без "ELSE", указав каждому блоку только действие в "THEN".

Тем не менее у нас остаётся "хвост" из тех же опкодов, которые изменяют работу алгоритма (машине задаётся всегда скорость 0, а актёру - точность 10). Чтобы алгоритм работал так же, как прежде, нам необходимо добавить "continue" в каждый блок:

    1. У нас по прежнему есть блок "ELSE", где проверяются различные состояния транспорта и актёров. В этом случае выполняем пункт 1, но уже с этими состояниями:

После этих блоков кода идёт код выше, с проверками на модель транспорта и актрёров, и без блоков "ELSE".

    1. Опять у нас остались разветвления. Что же, повторим пункт 1, где избавимся от блока "ELSE", осуществив проверки на существование транспорта и актёра:

    1. Осталось только 1 разбиение, где проверяется цель игрока. Возвращаемся к любимому пункту 1, и убираем блок "ELSE":

    1. Казалось, что последнее разветвление убрать уже не получится, так как выполняются два разных алгоритма. Но и тут я умудрился избавиться от надоедливого "ELSE".

Дело в том, что если у нас есть только два разных алгоритма, то после выполнения первого условия нам не нужно делать второй. Поэтому после выполнения кода при истинном значении условия опкода 00DF ставим любимый "continue" после выполнения алгоритма 1.

Вот так мне удалось сократить весь код:

Обратите внимание, что новый код увеличился на 4 строки. Мы пожертвовали ими ради того, чтобы код читался максимально легко. Если количество алгоритмов больше двух, то уже с "ELSE" можно жить. В общем прицнип работы здесь такой:

Как сократить код мы научились. Как же научится писать такой код? Берём условия, которые продолжат код. Инвертируем их и в "THEN" пишем "continue", а остальной код пишем уже после, не используя "ELSE". Вот и всё решение :)

Рассмотрим ситуацию, когда у нас есть цикл на метках. Там не всё так просто, так как нет встроенного "continue". Здесь в дело вступает опкод "004D", более известный как "JF". Если в нас нет каких-либо действий при выполнении условия, то лучше использовать прыжок на первую метку цикла. Если действия всё же есть, то используем "JUMP" на первую метку цикла.

Здесь важно то, что в отличии от циклов условия инвентировать не нужно. Пишем так, как обычно:

Что же, надеюсь, что этот материал был полезным и у Ваш код избавиться от лишних "ёлочек" и будет смотреться читабельно для любого пользователя.|39|1|0|48659987png600368320`196``||izbavljaemsja_ot_jolochek|1567358296

Last updated

Was this helpful?