1. введение в urbiScript — часть 1
2. введение в urbiScript — часть 2
3. введение в urbiScript — часть 3
Продолжаем рассмотрение возможностей скриптового языка urbiScript от компании Gostai, предназначенного для управления роботами.
Напомню, что urbiScript выполняется на Urbi-сервере, который запускается либо на роботе, либо на компьютере и к которому можно подключиться через сеть обычным telnet-ом.
16. Прототипное программирование в urbiScript
Для примера рассмотрим объект Pair — контейнер, который содержит две величины (first и second) и похож н std::pair в С++.
Pair; [00000000] (nil, nil)
Видим, что Pair содержит две переменных, которые равны nil (по-умолчанию).
Клонируем Pair в свою переменную:
var p = Pair.clone; [00000000] (nil, nil) p.first = "101010"; [00000000] "101010" p.second = true; [00000000] true p; [00000000] ("101010", true) Pair; [00000000] (nil, nil)
Т.к. Pair — это объект, то можно модифицировать его свойства. Но это отнюдь не лучшая идея, т.к. новые свойства будут изменять не только следующих производных от Pair, но и предыдущих:
var before = Pair.clone; [00000000] (nil, nil) Pair.first = false; [00000000] false var after = Pair.clone; [00000000] (false, nil) before; [00000000] (false, nil) // before и after имеют доступ к тому же значению first. assert(Pair.first === before.first); assert(Pair.first === after.first);
17. Псевдоклассы
Например, для определения своего класса Pair нужно создать объект с двумя слотами first и second (первый и второй).
Для этого можно использовать ключевое слово do
var MyPair = Object.clone; [00000000] Object_0x1 do (MyPair) { var first = nil; var second = nil; function asString () { "MyPair: " + first + ", " + second }; } | {}; // создали свой объект Pair MyPair; [00000000] MyPair: nil, nil // испытаем var p = MyPair.clone; [00000000] MyPair: nil, nil p.first = 0; [00000000] 0 p; [00000000] MyPair: 0, nil MyPair; [00000000] MyPair: nil, nil
то же самое можно проделать используя ключевое слово class:
class MyPair { var first = nil; var second = nil; function asString() { "(" + first + ", " + second + ")"; }; }; [00000000] (nil, nil)
Т.е. ключевое слово class просто создаёт объект MyPair, клонируя Object и предоставляет оболочку do.
18. Конструктор
Некоторые классы поддерживают конструкторы, доступные через метод new (вместо метода clone), принимающего передаваемые параметры.
var p = Pair.new("foo", false); [00000000] ("foo", false)
Т.о. получается, что clone гарантирует получение пустого объекта, наследуемого от прототипа.
метод new по-умолчанию делает то же самое, но позволяет сразу задавать новые атрибуты объекта.
Для определения конструктора, прототипу достаточно иметь функцию init, которая будет вызвана с параметрами, переданными методу new.
Добавим конструктор классу MyPair из предыдущего примера:
class MyPair { var first = nil; var second = nil; function init(f, s) { first = f; second = s; }; function asString() { "(" + first + ", " + second + ")"; }; }; [00000000] (nil, nil) MyPair.new(0, 1); [00000000] (0, 1)
19. Операторы
urbiScript поддерживает перегрузку операторов.
class ArithPair { var first = nil; var second = nil; function init(f, s) { first = f; second = s; }; function asString() { "(" + first + ", " + second + ")"; }; function '+'(rhs) { new(first + rhs.first, second + rhs.second); }; function '-'(rhs) { new(first - rhs.first, second - rhs.second); }; function '*'(rhs) { new(first * rhs.first, second * rhs.second); }; function '/'(rhs) { new(first / rhs.first, second / rhs.second); }; }; [00000000] (nil, nil) ArithPair.new(1, 10) + ArithPair.new(2, 20) * ArithPair.new(3, 30); [00000000] (7, 610)
20.
Для того чтобы вставить в объект функцию нужно использовать метод setSlot:
var o = Object.clone | {}; // Here we can use f as any regular value o.setSlot("m1", function () { echo("Hello") }) | {}; // This is strictly equivalent var o.m2 = function () { echo("Hello") } | {}; o.m1; [00000000] *** Hello o.m2; [00000000] *** Hello
Это позволяет писать очень интересный код, например функцию, которая в качестве аргумента принимает другую функцию.
Пример — функция all(), которая принимает в качестве параметров список и функцию и для каждого элемента списка вызывает данную функцию
function all(list, predicate) { for (var elt : list) if (!predicate(elt)) return false; return true; } | {}; // проверим являются ли все элементы списка положительными function positive(x) { x >= 0 } | {}; all([1, 2, 3], getSlot("positive")); [00000000] true all([1, 2, -3], getSlot("positive")); [00000000] false
Метод all уже существует. Его можно вызвать
list.all(predicate)
21. Лямбда-функции
Лямбда-функция — это функция не привязанная к конкретному имени (их удобно использовать в функциях в качестве callback-параметра ).
Пример создания такой анонимной функции в urbiScript:
// создание анонимной функции function (x) {x + 1} | {}; // создание такой функции позволяет на лету // передать её в качестве параметра метода "all": [1, 2, 3].all(function (x) { x > 0}); [00000000] true
22. Ленивые аргументы (Lazy arguments)
в urbiScript по-умолчанию аргументы функции задаются точно, но с помощью объекта call можно вручную получить доступ к переданным аргументам функции.
// аргументы функции не указываются function first { // вызываем первый аргумент call.evalArgAt(0); } | {}; first(echo("first"), echo("second")); [00000000] *** first function reverse { call.evalArgAt(1); call.evalArgAt(0); } | {}; reverse(echo("first"), echo("second")); [00000000] *** second [00000000] *** first
Отличный пример — логический оператор:
function myAnd { if (call.evalArgAt(0)) call.evalArgAt(1) else false; }|; function f() { echo("f executed"); return true; }|; myAnd(false, f()); [00000000] false myAnd(true, f()); [00000000] *** f executed [00000000] true
Читать далее: введение в urbiScript — часть 4