Этот пост будет интересен только ограниченному кругу людей. Если вы не знакомы со скриптингом Mikrotik или никогда не использовали функции, то, скорее всего, это сообщение не для вас, но всё равно можете почитать.
Я рассмотрю два оператора, которые нашёл, нажав F1, находясь в скобках. Первый оператор называется quote. Это унарный оператор ‘>’, который работает точно так же, как quote в LISP, если кто-то знаком с этим. Он возвращает то, к чему применяется, как литерал. Даже код. Это значит, что его можно использовать как альтернативный способ создания функций.
Вот все три способа создания функции, которые я знаю, включая этот новый оператор в действии:
:global func1 do={:put "test1"}
:global func2 [parse ":put \"test2\""]
:global func3 (>[:put "test3"])
Интересно, что все три метода дают немного разные результаты. Вот что вы получаете, если вывести их все:
put $func1
# ;(evl (evl /putmessage=test1))
put $func2
# (evl /putmessage=test2)
put $func3
# (evl (evl /putmessage=test3))
func1 — это массив из 2 элементов, где второй — это объект кода. Два других — сами объекты кода. Если вы не понимаете, о чём речь, попробуйте применить typeof к ним. Практически все они обрабатываются одинаково, и не имеет значения, какой способ вы используете для создания функций. Технически parse даёт небольшое повышение производительности и занимает чуть меньше памяти, потому что нет лишних evl и массивов, но это, скорее всего, не имеет значения для большинства задач.
Я точно ещё не полностью изучил возможности этого оператора, например, не пробовал использовать его со строкой, в которой есть вставки команд — это может быть интересно. Но я двигаюсь дальше.
Следующий оператор, о котором я расскажу, называется «activate in environment». Я впервые наткнулся на него в июне прошлого года, когда коллега оставил о нём пост на форуме, и даже rextended был сбит с толку, так что я отложил это. Но теперь я наконец разгадал его, и мне кажется, что он довольно крутой.
Оператор выглядит так: <%%. Он требует небольшого объяснения. Под «окружением» здесь понимается просто «набор доступных переменных». Этот оператор запускает объект кода слева с добавлением массива справа в его окружение.
Например, допустим, вы создаёте следующую функцию:
:global add (>[:return ($0+$1)])
# Теперь её можно вызвать так, чтобы вывести 3
:put [$add 1 2]
# Чтобы использовать новый оператор с тем же результатом, делаем так:
:put ($add <%% {1;2})
Правка: использовать скобки здесь не получится, потому что $0 — имя функции. Надо использовать $1+$2, и окружение, переданное в <%%, было бы {“add”;1;2} для эквивалентности.
Если вы внимательно посмотрели, то заметили, что содержимое массива обращается через $0 и $1. Массив, который передаётся, становится окружением (набором переменных) для функции, так что нулевой элемент доступен через $0, первый — через $1.
Сам по себе этот приём не очень мощный, так что давайте посмотрим на другой пример:
:global f (>[:put $output])
# Мы нигде не определяли output. Его нет. И всё же:
($f <%% {output="Я существую!"})
# Это добавит переменную output из массива в окружение, позволяя функции получить к ней доступ
# Сейчас это всё равно что
[$f output="Я существую!"]
Это гораздо интереснее! Мы можем создавать объекты, которые инкапсулируют параметры функции, и передавать их функции. Уверен, это уже вызывает у некоторых идеи. Но если вам это понравилось, у меня есть пример, который вас точно удивит.
Что если я скажу, что массив, который вы передаёте, передаётся по ссылке?
:global inc (>[:set $counter ($counter+1)])
:global c {counter=0}
# Что, как вы думаете, произойдёт здесь?
($inc <%% $c)
# Здесь уже нет эквивалента с использованием скобок
# Это уже НЕ эквивалент
[$inc $c]
Поверьте или нет, это работает именно так, как вы думаете. Выведите значение $c после этого — увидите, что counter действительно увеличился до 1. Теперь мы можем передавать объект (массив), работать с его переменными и сохранять изменения. Это новая функциональность, уникальная для этого оператора. Это почти как объектно-ориентированное программирование, хотя и не настолько мощное.
Я понимаю, почему Mikrotik не документирует эту возможность и не ожидаю, что так будет в будущем. Это нишевая функция, и лишь немногие пользователи смогут найти для неё полезное применение. Я даже не утверждаю, что у меня оно есть. Но это ещё один интересный инструмент для написания нестандартных скриптов, и я с нетерпением жду, какие возможности он откроет.
Я рассмотрю два оператора, которые нашёл, нажав F1, находясь в скобках. Первый оператор называется quote. Это унарный оператор ‘>’, который работает точно так же, как quote в LISP, если кто-то знаком с этим. Он возвращает то, к чему применяется, как литерал. Даже код. Это значит, что его можно использовать как альтернативный способ создания функций.
Вот все три способа создания функции, которые я знаю, включая этот новый оператор в действии:
:global func1 do={:put "test1"}
:global func2 [parse ":put \"test2\""]
:global func3 (>[:put "test3"])
Интересно, что все три метода дают немного разные результаты. Вот что вы получаете, если вывести их все:
put $func1
# ;(evl (evl /putmessage=test1))
put $func2
# (evl /putmessage=test2)
put $func3
# (evl (evl /putmessage=test3))
func1 — это массив из 2 элементов, где второй — это объект кода. Два других — сами объекты кода. Если вы не понимаете, о чём речь, попробуйте применить typeof к ним. Практически все они обрабатываются одинаково, и не имеет значения, какой способ вы используете для создания функций. Технически parse даёт небольшое повышение производительности и занимает чуть меньше памяти, потому что нет лишних evl и массивов, но это, скорее всего, не имеет значения для большинства задач.
Я точно ещё не полностью изучил возможности этого оператора, например, не пробовал использовать его со строкой, в которой есть вставки команд — это может быть интересно. Но я двигаюсь дальше.
Следующий оператор, о котором я расскажу, называется «activate in environment». Я впервые наткнулся на него в июне прошлого года, когда коллега оставил о нём пост на форуме, и даже rextended был сбит с толку, так что я отложил это. Но теперь я наконец разгадал его, и мне кажется, что он довольно крутой.
Оператор выглядит так: <%%. Он требует небольшого объяснения. Под «окружением» здесь понимается просто «набор доступных переменных». Этот оператор запускает объект кода слева с добавлением массива справа в его окружение.
Например, допустим, вы создаёте следующую функцию:
:global add (>[:return ($0+$1)])
# Теперь её можно вызвать так, чтобы вывести 3
:put [$add 1 2]
# Чтобы использовать новый оператор с тем же результатом, делаем так:
:put ($add <%% {1;2})
Правка: использовать скобки здесь не получится, потому что $0 — имя функции. Надо использовать $1+$2, и окружение, переданное в <%%, было бы {“add”;1;2} для эквивалентности.
Если вы внимательно посмотрели, то заметили, что содержимое массива обращается через $0 и $1. Массив, который передаётся, становится окружением (набором переменных) для функции, так что нулевой элемент доступен через $0, первый — через $1.
Сам по себе этот приём не очень мощный, так что давайте посмотрим на другой пример:
:global f (>[:put $output])
# Мы нигде не определяли output. Его нет. И всё же:
($f <%% {output="Я существую!"})
# Это добавит переменную output из массива в окружение, позволяя функции получить к ней доступ
# Сейчас это всё равно что
[$f output="Я существую!"]
Это гораздо интереснее! Мы можем создавать объекты, которые инкапсулируют параметры функции, и передавать их функции. Уверен, это уже вызывает у некоторых идеи. Но если вам это понравилось, у меня есть пример, который вас точно удивит.
Что если я скажу, что массив, который вы передаёте, передаётся по ссылке?
:global inc (>[:set $counter ($counter+1)])
:global c {counter=0}
# Что, как вы думаете, произойдёт здесь?
($inc <%% $c)
# Здесь уже нет эквивалента с использованием скобок
# Это уже НЕ эквивалент
[$inc $c]
Поверьте или нет, это работает именно так, как вы думаете. Выведите значение $c после этого — увидите, что counter действительно увеличился до 1. Теперь мы можем передавать объект (массив), работать с его переменными и сохранять изменения. Это новая функциональность, уникальная для этого оператора. Это почти как объектно-ориентированное программирование, хотя и не настолько мощное.
Я понимаю, почему Mikrotik не документирует эту возможность и не ожидаю, что так будет в будущем. Это нишевая функция, и лишь немногие пользователи смогут найти для неё полезное применение. Я даже не утверждаю, что у меня оно есть. Но это ещё один интересный инструмент для написания нестандартных скриптов, и я с нетерпением жду, какие возможности он откроет.

