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
