http://man.deeptown.org/api.php?action=feedcontributions&user=Raw+mat&feedformat=atomDeeptown Manual - Вклад участника [ru]2024-03-29T01:42:37ZВклад участникаMediaWiki 1.20.2http://man.deeptown.org/index.php/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8Функции2013-07-13T12:42:17Z<p>Raw mat: </p>
<hr />
<div>Ни один современный язык программирования не был бы возможен без функций. Функции это "кирпичики", из которых складывается программа. Для работы функциям, подобно обычным программам могут потребоваться входные данные — их называют аргументами, или параметрами функции. Функции так же могут возвращать результат. С этой точки зрения, функцию можно сравнить с "черным ящиком", имеющим несколько входов и один выход. Однако, в отличие классического "черного ящика", функция так же может изменять сами входные значения. Но в целом, смысл функций тот же, что и в математике. <br />
<br />
С точки зрения языка К++, каждая функция представляет собой подпрограмму, то есть некоторый участок кода, который функионирует автономно. Сами функции а так же области и примеры их применения были неоднократно рассмотрены в предыдущих главах книги. В этой главе внимание будет уделено синтаксису функций, способам их объявления и некоторым моментам, связанным с их применением.<br />
<br />
== Объявление ==<br />
<br />
Объявление любой функции начинается с указания ключевого слова <tt>'''function'''</tt> следом за которым указывается тип возвращаемого значения, после которого идет [[идентификатор]] имени функции. Указание типа можно опустить, тогда будет подразумеваться, что функция возвращает [[Переменные#Нетипированные (динамические) переменные|переменную динамического типа]]. После указания имени функции идет блок описания ''параметров'', или ''аргументов'' функции. Блок заключается в круглые скобки; сами параметры, в ходе описания отделяются друг от друга запятой. Завершает конструкцию ''тело функции'', которое записывается в фигурных скобках. <br />
<br />
Вот примеры объявления функций:<br />
<source lang="kpp"><br />
function MyFunction() { return 5; }<br />
function Compare(x, y) { return x < y; }<br />
</source><br />
<br />
Первая функция не принимает параметров, но возвращает числовую константу 5. Согласитесь, не очень полезный код. Вторая функция немного "поумнее": она принимает два объекта и пытается их сравнить, используя оператор отношения "меньше". Результатом выполнения такой функции будет логическое значение "истина", если ''x'' и вправду меньше чем ''y'', либо ложь в противном случае. Для правильной работы этой функции требуется, чтобы передаваемые параметры допускали возможность сравнения (то есть, были бы определены соответствующие [[Выражения#Операторы|операторы]]). Если этого нет, — будет сгенерировано [[Идеология языка#Понятие исключения|исключение]], то есть ошибка времени исполнения. Поскольку типы параметров никак не указаны, компилятор не имеет возможности контролировать фактические типы передаваемых параметров, а следовательно не может предупредить программиста если по его мнению что-то не так. Тем не менее, это обеспечивает программиста возможностью написания гибких программ и функций, которые не зависят от конкретных типов передаваемых данных.<br />
<br />
Несмотря на простоту последней функции, подобный код может с успехом применяться в реальных программах. Пример такого кода будет приведен при описании [[Блоки|блоков]], чуть дальше по ходу книги.<br />
<br />
== Аргументы ==<br />
<br />
Аргументы функции — это та информация, которую программист хочет передать в функцию ее для последующей обработки. Как уже было показано ранее, в качестве аргументов функций могут передаваться любые объекты, любых типов. При этом, будет генерироваться динамический код, не привязанный к конкретным типам данных. Такие функции могут применяться в случаях, когда они должны принимать в качестве параметров целый набор объектов различных типов. Чтобы повысить надежность кода (а также дать компилятору информацию для оптимизации), следует применять типизацию аргументов (рекомендуется).<br />
<br />
=== Типизация аргументов ===<br />
<br />
Если функция подразумевает передачу параметров строго определенного типа, то применяется расширенная форма записи аргументов. При этом, идентификатор имени параметра предворяется именем типа, который следует принимать. Например, вышеописанную функцию <tt>Compare()</tt> мы можем переписать так, чтобы она принимала в качестве параметров только объекты, представляющие собой целые числа. При этом, имена переменных ''x'' и ''y'' мы дополняем сведениями о типе:<br />
<br />
<source lang="kpp"><br />
function Compare(int x, int y) { return x < y; }<br />
</source><br />
<br />
Теперь, в целях уменьшения вероятности ошибок, при компиляции вызовов такой функции, комилятор будет проверять соответствие типов фактически переданных параметров и типов, указанных в объявлении функции. Если типы различны, то компилятор попытается выполнить операцию ''приведения типов'', если же типы неприводимы — будет сгенерирована ошибка времени компиляции.<br />
<br />
В общем случае, в описании функции можно указывать параметры любых типов, и даже смешивать типированные и нетипированные параметры:<br />
<br />
<source lang="kpp"><br />
function MyFunction(int p1, string p2, block p3) { /* тело функции */ }<br />
function OtherFunction(MyClass p1, p2) { /* тело функции */ }<br />
</source><br />
<br />
Как видно из кода, функция <tt>MyFunction()</tt> имеет три параметра: [[Стандартные типы данных#Целые числа|целочисленный]] ''p1'', [[Стандартные типы данных#Строки|строковый]] ''p2'' и [[Блоки|блок]] ''p3''. <br />
<br />
Функция <tt>OtherFunction()</tt>, в качестве параметров может принимать экземпляры класса <tt>MyClass</tt> (параметр ''p1''), и объекты любого типа в качестве параметра ''p2''. '''Обратите внимание''', что может показаться, что тип <tt>MyClass </tt> указан для обоих аргументов, но на самом деле это не так. Тип привязывается только к переменной, указанной сразу после него. Более подробно, эта проблема рассмотрена в главе [[Объявление переменных и констант]].<br />
<br />
Приведем несколько примеров вызова функции с различными наборами параметров:<br />
<source lang="kpp" line="1"><br />
var myblock = { |x| x += 2; };<br />
MyFunction(10, "hello", myblock); //верно, типы фактических параметров совпадают<br />
MyFunction("20", 10, myblock); //частично верно, выполняется операция приведения<br />
MyFunction([1,2,3], myblock, 5); //неверно. переданы неприводимые типы<br />
<br />
var o1 = new MyClass;<br />
var o2 = new OtherClass;<br />
OtherFunction(o1, o2);<br />
OtherFunction(o2, o1);<br />
OtherFunction(o1, o1);<br />
OtherFunction(o2, o2);<br />
</source><br />
<br />
<br />
;1: Мы создаем экземпляр переменной-[[Блоки|блока]], который будет передаватсья в качестве параметра функциям. Здесь он не имеет особого значения, так что на него можно практически не обращать внимания (важен только его тип).<br />
<br />
;2-4: Производятся вызовы функции <tt>MyFunction()</tt> с различными наборами параметров. В первом вызове типы фактических параметров точно совпадают с типами в описании функции, следовательно никакого приведения не происходит и все работает как есть. Во втором вызове происходит приведение строковой константы "20" к числу, а числа 10 к строке. "Частично" верна эта конструкция потому, что на момент компиляции невозможно определить, сработает ли первая операция приведения (строки к числу) или нет. Если строка содержит нецифровые символы, то в результате операции приведения будет сгенерировано [[Идеология языка#Понятие исключения|исключение]]. Разумеется в данном случае, все будет хорошо. Второе приведение, а именно числа 20 к строке так же выполнится успешно, потому что абсолютно любое число можно представить в виде строки символов, соответствующих цифрам числа.<br />
<br />
: Третий вызов функции <tt>MyFunction()</tt> является совершенно неверным, поскольку все фактические параметры переданные в функцию, являются неприводимыми к соответствующим типам формальных параметров.<br />
<br />
;6-11: Здесь создаются две инстанции классов <tt>MyClass</tt> и некоторого класса <tt>OtherClass</tt>. Далее выполняется вызов функции <tt>OtherFunction()</tt>, которой в разных комбинациях передаются вышесозданные объекты. В качестве упражнения, Читателю предлагается самому решить, какие из вызовов верные а какие нет. <br />
<br />
: Постарайтесь ответить на следующие вопросы:<br />
:* Какие из вызовов приведут к ошибке времени компиляции?<br />
:* Какие из вызовов приведут к [[Идеология языка#Понятие исключения|исключению]] (ошибке времени исполнения)?<br />
:* Что изменится, если реализовать оператор приведения типа <tt>MyClass</tt> к <tt>OtherClass</tt>?<br />
:* Что изменится, если реализовать оператор приведения типа <tt>OtherClass</tt> к <tt>MyClass</tt>?<br />
:* Что изменится, если типы будут взаимноприводимыми?<br />
<br />
=== Инициализаторы аргументов (значения по умолчанию) ===<br />
<br />
В некоторых случаях может возникнуть необходимость задания параметров функции по умолчанию, либо возможность указания только части параметров. Например, функция может иметь обширный список аргументов, большая часть которых обычно не используется. То есть, они влияют на некоторые очень специфичные свойства объекта, которые редко применяются. Если такую функцию описывать традиционным образом, то получится что программист, использующий ее в своей программе, должен будет каждый раз писать что-то вроде:<br />
<source lang="kpp"><br />
var x = SomeWierdFunction(1, "a", 0, 0, 0, 0, 0, 0);<br />
...<br />
var y = SomeWierdFunction(2, "b", 0, 0, 0, 0, 0, 0);<br />
...<br />
</source><br />
<br />
Такие вызовы портят внешний вид кода и усложняют его написание (особенно если параметры по умолчанию далеко не такие простые, как в этом примере). Желательно было бы сделать так, чтобы функция сама "знала", какие значения параметров нужно указать, если в коде вызова они были опущены. Для этой цели применяются инициализаторы. Инициализаторы аргументов (их еще называют "значениями по умолчанию"), подобно инициализаторам обычных переменных устанавливают начальное значение аргумента, но только если оно не было установлено явным образом при вызове данной функции. <br />
<br />
Приведем более приближенный к реальности пример. Допустим, программист решил написать свою реализацию некоторого класса контейнера, служащего для содержания других объектов. Предположим так же, что данный класс подобно [[Стандартные типы данных#Массивы и списки|массиву]] позволяет обратиться к своим элементам через индекс и имеет некоторое свойство, показывающее текущий размер коллекции. Программист желает написать функцию, возвращающую некоторое подмножество данной коллекции, расположенное между верхним и нижним индексами. Логично предположить, что может возникнуть желание указать только один из индексов, в то время как другой должен подставиться как граница коллекции с соответствующей стороны. То есть, в случае нижнего индекса, граничное значение будет равно нулю, в то время как граничное значение для верхнего индекса заранее не определено. Фактически, оно равно количеству размещенных в коллекции элементов минус один (если нумерация ведется с нуля). Разумеется, это число меняется по мере добавления и удаления элементов. Если бы инициализаторов не существовало, программисту приходилось бы каждый раз явно указывать верхнюю границу выборки на основании его представления о текущем количестве элементов в коллекции (еще одно потенциальное место для ошибки). Это по меньшей мере неудобно. По большей — невозможно, ведь не всегда заранее известно, какое количество элементов содержится в коллекции в данный момент. В общем, с учетом существования инициализаторов, интерфейс класса коллекции мог бы выглядеть примерно так:<br />
<br />
<source lang="kpp"><br />
class MyCollection {<br />
public function int Add(...); //добавление элемента<br />
private function GetLength(); // размер коллекции<br />
property length read GetLength;<br />
const function MyCollection SubCollection(int from = 0, int to = this.length - 1);<br />
}<br />
</source><br />
<br />
В первой строке объявляется метод добавления элемента в массив. Для удобства применения класса, он оформлен в виде функции с переменным списком аргументов (см. ниже). Если говорить коротко, то это функция, которая может принимать различное (сколь угодно большое) количество параметров в зависимости от вызова. При этом, они не описываются в заголовке функции, а вместо них ставится ''оператор-многоточие'' (<tt>...</tt>). Это необходимо, чтобы отличить такую функцию от обычной функции. При вызове, фактические параметры автоматически добавляются в массив, который можно использовать внутри функции.<br />
<br />
Далее следуют аксессор, возвращающий текущее количество элементов в коллекции и свойство, связанное с ним.<br />
<br />
Последняя строка как раз представляет для нас интерес. Как видно из описания, метод <tt>SubCollection()</tt> возвращает некоторе подмножество класса, которое само является экземпляром данного класса; границы выборки задаются двумя параметрами: нижним индексом ''from'' и верхним ''to''. Аргументы, соответственно, инициализированы нулем и выражением доступа к свойству <tt>length</tt>, которое будет возвращать значение верхнего индекса. Поскольку в выражении есть ссылка на текущий экземпляр (<tt>'''this'''</tt>), то значение выражения будет зависеть от того, метод какого экземпляра вызывается в каждом конкретном случае.<br />
<br />
Вот пример некоторого участка программы, использующей вышеописанный класс:<br />
<source lang="kpp"><br />
var c = new MyCollection;<br />
MyCollection.Add(1, "a", [2], { |x| x + 1; }, { 1 => 'a', 2 => 'b'});<br />
var sub1 = c.SubCollection();<br />
var sub2 = c.SubCollection(3);<br />
var sub3 = c.SubCollection(1, 4);<br />
</source><br />
<br />
=== Модификаторы и копирование ===<br />
<br />
Довольно часто создаются функции, принимающие в качестве параметров не отдельные переменные, вроде чисел строк или блоков, а целые массивы. Например, программисту может потребоваться написать функцию, рассчитывающую математическое ожидание некоторой величины. При этом, в качестве параметров функция будет принимать массив значений и массив соответствущих значениям вероятностей; вычисленное значение математического ожидания возвращается в качестве значения функции.<br />
<br />
Математическое ожидание, это величина соответствующая наиболее вероятному из значений исходной величины. Для набора дискретных величин (а именно такой случай мы и рассматриваем) она рассчитывается, как сумма произведений значения величины на ее вероятность:<br />
<br />
:[[Изображение:DMX.png]] <!--:<math>MX = \sum\limits_{i=1}^n x_i \cdot p_i</math>--><br />
<br />
Приведем пример функции, реализующей эту формулу:<br />
<source lang="kpp" line="1"><br />
function real AOD(array values, array probs) {<br />
if (values.size != probs.size) //проверка исходных данных<br />
throw "массивы должны иметь одинаковое количество элементов";<br />
<br />
var real M; //счетчик и накопитель для величины математического ожидания<br />
for (var i = 0; i < values.size; ++i)<br />
M += values[i] * probs[i];<br />
return M;<br />
}<br />
</source><br />
<br />
;1-2: Для того чтобы значение можно было рассчитать, размеры исходных массивов должны совпадать. Если они не совпадают, то генерируется [[Идеология языка#Понятие исключения|исключение]] с соответствующим сообщением об ошибке.<br />
<br />
;4: Здесь мы объявляем переменную счетчик, которая будет использоваться в цикле и переменную, накапливающую сумму, то есть конечную величину математического ожидания. <br />
<br />
;5-8: Для рассчета значения применяется цикл <tt>'''for'''</tt>, который перебирает переменную-счетчик ''i'' начиная с нуля и заканчивая последним индексом массивов. Тело цикла представляет собой вышеописанную формулу. Результат возвращается с помощью оператора <tt>'''return'''</tt>.<br />
<br />
<br />
Теперь, если мы создадим два массива и заполним их некоторыми значениями, то мы сможем вычислить для них значение математического ожидания:<br />
<source lang="kpp"><br />
var v = [-3, -2, -1, 0, 1, 2, 3 ]; //значения<br />
var p = [0.01, 0.05, 0.2, 0.3, 0.35, 0.05, 0.04]; //вероятности<br />
var avg = AOD(v, p);<br />
</source><br />
<br />
С точки зрения математики вышеприведенный пример верен. Однако, существует одна проблема, которая может существенно сказаться на производительности работы системы и на объемах используемой памяти. Дело заключается в том, что при передаче параметров происходит копирование объекта параметра с передачей в функцию уже его копии. Это сделано для того, чтобы функция могла свободно распоряжаться параметрами, как обычными локальными переменными, без опасности случайно изменить значение внешней переменной. <br />
<br />
Таким образом, в вышеприведенном примере при вызове функции <tt>AOD()</tt>, будет произведено копирование двух переданных массивов, а значит и всех их элементов. Естественно, чем больше размеры массивов, тем больший объем данных приходится копировать. На практике, статистические массивы могут иметь тысячи и даже десятки тысяч значений!<br />
<br />
Чтобы решить эту проблему, мы должны указать компилятору, что объекты параметров копировать не нужно. Мы, в свою очередь "обещаем" не изменять их значения, так что вызывающий код будет "спокоен", что с переданными объектами ничего не случится. Это осуществляется с помощью ключевого слова-спецификатора <tt>'''const'''</tt>, которое указывается в блоке описания параметров непосредственно перед самим параметром. Оно указывает, что данный параметр не будет изменяться в функции, что позволит компилятору произвести соответствующую оптимизацию. При этом, внутрь функции будет передан сам объект, точнее ссылка на него. Разумеется, компилятор так же следит за тем, чтобы в коде функции не было попыток изменить значение такого параметра, а если это все же случится, — будет сгенерирована ошибка времени компиляции. <br />
<br />
Перепишем вышеприведенный пример с использованием спецификаторов <tt>'''const'''</tt>:<br />
<source lang="kpp"><br />
function real AOD(const array values, const array probs) {<br />
if (values.size != probs.size) //проверка исходных данных<br />
throw "массивы должны иметь одинаковое количество элементов";<br />
<br />
var real M; //счетчик и накопитель для величины математического ожидания<br />
for (var i = 0; i < values.size; ++i)<br />
M += values[i] * probs[i];<br />
return M;<br />
}<br />
</source><br />
<br />
В вышеописанной функции, массивы используются только для вычисления значения математического ожидания и их изменения не происходит, так что применение спецификатора никак не скажется на реализации самой функции (тело функции фактически осталось неизмененным), но теперь код сможет работать намного быстрее (при большом количестве элементов). Это как раз тот случай, когда внешне незначительные изменения программы существенно влияют на ее производительность. Поэтому, хороший программист всегда должен держать в уме особенности конкретного языка программирования и учитывать их при написании программ.<br />
<br />
<br />
Но что если нам необходимо решить прямо противоположную задачу? А именно, передать в функцию некоторый объект, для его изменения внутри функции. Если просто написать код вида:<br />
<source lang="kpp"><br />
function mutate(int x) { <br />
puts("x = #{x}"); // вывод: x = 5<br />
x = 10; <br />
puts("x = #{x}"); // вывод: x = 10<br />
}<br />
...<br />
var v = 5;<br />
mutate(v);<br />
puts("v = #{v}"); // вывод: v = 5<br />
</source><br />
...то ничего не получится. Да, переменная ''v'' будет передана внутрь функции <tt>mutate()</tt>. Но при передаче параметра произойдет его копирование. Таким образом, внутри функции будет изменено значение копии, которая будет уничтожена при выходе из функции. Изначальная переменная как была установлена, так и осталась равной пяти.<br />
<br />
Чтобы указать компилятору, что в функцию следует передавать сам объект, а не его копию — следует указать ключевое слово-спецификатор <tt>'''var'''</tt> (по аналогии с <tt>'''const'''</tt>). При этом, копироваться аргумент не будет но будет возможно его изменение в теле функции:<br />
<br />
<source lang="kpp"><br />
function mutate(var int x) { <br />
puts("x = #{x}"); // вывод: x = 5<br />
x = 10; <br />
puts("x = #{x}"); // вывод: x = 10<br />
}<br />
...<br />
var v = 5;<br />
mutate(v);<br />
puts("v = #{v}"); // вывод: v = 10<br />
</source><br />
<br />
=== Функции с переменным списком аргументов ===<br />
<br />
Бывают случаи, когда заранее не известно, какое количество параметров должна принимать функция. В качестве примера можно привести задачу форматируемого вывода. Форматируемый вывод является удобным средством формирования строки, содержащей как текст так и значения некоторых переменных, связанных с этим текстом. Предположим, что на основании статистических данных, нам надо вывести строку следующего содержания:<br />
<br />
Пользователь vpupkin (501) обращался к файлу '~/docs/index.txt' 122 раз(а).<br />
<br />
Данная строка содержит как чисто текстовую информацию, так и значения, которые мы должны "внедрить" в текст. "Лобовое" решение проблемы может выглядеть примерно так:<br />
<br />
<source lang="kpp"><br />
var myfile = '~/docs/index.txt'; //имя файла<br />
var fstat = File.stat(myfile); //получение информации о файле<br />
var uname = 'vpupkin'; //имя пользователя<br />
var userid = 501; //системный идентификатор пользователя<br />
print("Пользователь " + uname + "(" + userid + <br />
") обращался к файлу '" + myfile + "' " + <br />
fstat.times_accessed(userid) + " раз(а).\n");<br />
</source><br />
<br />
Согласитесь, функция вывода выглядит не очень привлекательно. Она изобилует операторами конкатенации строк и прочей информацией, которая разбросана по всему вызову. Попробуем ее переписать с помощью ''оператора-вставки'' (см. главу [[Стандартные типы данных#Строки|Стандартные типы данных]]): <br />
<br />
<source lang="kpp"><br />
print("Пользователь #{uname} (#{userid}) обращался к файлу #{myfile} " + <br />
"#{fstat.times_accessed(userid)} раз(а).\n");<br />
</source><br />
<br />
В такой реализации, код стал намного более читаемым, однако есть еще одна проблема. Что если впоследствии потребуется написать версию программы, поддерживающую другой язык интерфейса? Пришлось бы либо писать свою копию программы для каждой языковой версии, либо каким-то образом отделить строковые данные от вставок и вынести их в отдельный файл ресурсов. Тогда, для того чтобы перевести интерфейс программы на другой язык, достаточно будет всего лишь отредактировать файл ресурсов, без необходимости изменения текстов программы.<br />
<br />
Хорошим решением для этого является форматируемый вывод. При этом, операция вывода сводится к указанию строки-''формата'', а так же набора значений переменных, которые необходимо внедрить в текст. Для вышеприведенного примера, строки формата в зависимости от языка будут выглядеть так:<br />
<br />
Пользователь % (%d) обращался к файлу '%' %d раз(а).\n<br />
User % (%d) accessed file '%' %d times.\n<br />
<br />
Каждый символ <tt>%</tt> соответствует одному значению переменной из последующего списка. При разборе (форматировании) такой строки, каждый символ <tt>%</tt> будет замещен на значение соответствующей переменной. Для вывода самого символа % применяется escape последовательность из двух подряд идущих символов <tt>%%</tt>. Пара символов <tt>%d</tt> в случае строки форматирования, означает необходимость выполнения приведения типа числа к строке.<br />
<br />
Таким образом, в функцию форматирования необходимо передать как минимуму один параметр — формат. Дальнейшие параметры и их количество будут зависеть уже от самой строки формата. Но как это описать в терминах языка программирования? Одним из решений может быть передача в функцию [[Стандартные типы данных#Массивы и списки|массива]] с параметрами. Тогда, код описания функции форматируемого вывода и ее вызова может выглядеть так:<br />
<br />
<source lang="kpp"><br />
function printf(const string format, const array args);<br />
//...<br />
printf("Пользователь % (%d) обращался к файлу '%' %d раз(а).\n", <br />
[uname, userid, myfile, fstat.times_accessed(userid)]);<br />
</source><br />
<br />
Для вывода результирующей строки, ищутся вхождения символов <tt>%</tt> (с возможными спецификаторами, как в случае с <tt>%d</tt>) в строке формата и подставлять значение текущего элемента массива. После подстановки, текущим элементом становится следующий элемент массива. <br />
<br />
Более красивое решение заключается в использовании ''оператора-многоточие'' (<tt>...</tt>) в объявлении функции, который указывет, что функция должна принимать переменное количество аргументов:<br />
<br />
<source lang="kpp"><br />
function printf(const string format, ...);<br />
//...<br />
printf("Пользователь % (%d) обращался к файлу '%' %d раз(а).\n", <br />
uname, userid, myfile, fstat.times_accessed(userid));<br />
</source><br />
<br />
Теперь, код вызова такой функции ничем не отличается от вызова любой другой функции или метода. Программисту не нужно помнить об особенностях функции — нужно просто записывать параметры один за одним. Обладая всей необходимой информацией, компилятор языка сам сформирует массив передаваемых параметров, который будет передан в функцию. Для разбора аргументов внутри функции используется специальный метод <tt>args()</tt>, возвращающий массив аргументов.<br />
<br />
'''Примечание 1:''' на самом деле, приведенная в примерах функция <tt>printf()</tt>, реализована как расширение класса <tt>[[Стандартные типы данных#Строки|string]]</tt> стандартной билбиотеки языка К++. Желающие реализовать форматируемый вывод могут ее использовать. Здесь же она была приведена, как наиболее подходящий пример функции с переменным списком аргументов.<br />
<br />
'''Примечание 2:''' В некоторых случаях заранее неизвестно, какие именно наборы параметров будут востребованы больше других, с тем чтобы переместить их ближе к началу описания. Либо метод принимает большое количество параметров, которые могут использоваться "стихийно". Примерами таких методов могут послужить методы открытия соединения. Такие методы могут принимать большое количество параметров, как то:<br />
* адрес назначения<br />
* порт назначения<br />
* данные авторизации (логин, пароль)<br />
* настройки сжатия<br />
* настройки шифрования<br />
* таймаут соедиенения<br />
* прочая информация<br />
<br />
Реально из всей этой кучи, могут использоваться 2-3 параметра, в то время как остальные остаются по умолчанию. Проблема заключается в том, что не всегда возможно описать параметры так чтобы необходимые значения возможно было передавать по порядку. Если же требуется задействовать параметр из конца списка, придется указывать и промежуточные параметры, устанавливая их значения в <tt>'''null'''</tt>. Это засоряет код и может привести к ошибкам.<br />
<br />
Решением этой ситуации может послужить использование [[Стандартные типы данных#Хеши|хешей]] для передачи только тех параметров, которые необходимы. Тогда наша функция объявляется таким образом:<br />
<source lang="kpp"><br />
function connect(const hash params);<br />
</source><br />
<br />
В теле функции мы работаем с хешем ''params'' доставая из него значения по мере необходимости. При отсутствии некоторой записи в хеше, считается что необходимо использовать значение по умолчанию.<br />
<br />
Вызов такого метода может осуществляться либо традиционным образом, когда хеш создается за пределами метода, а последнему передается сама переменная, либо с помощью специального синтаксиса:<br />
<source lang="kpp"><br />
var params = { :address => 'www.deeptown.org', <br />
:port => 1234, :login => 'guest' };<br />
connect(params); //традиционный способ вызова<br />
connect(:address => 'www.deeptown.org', :timeout => 20); //альтернативный<br />
</source><br />
<br />
Как видно из примера, в последней строке происходит вызов метода, одновременно с объявлением хеша "на месте". Такая форма подразумевает, что соответствующая функция должна иметь хеш в качестве последнего параметра.<br />
<br />
== Возврат значения ==<br />
<br />
Для выхода из функции применяется оператор <tt>'''return'''</tt>, после которого указывается выражение, значение которого необходимо вернуть в качестве значения функции. Если функция ничего не возвращает, то есть тип результата определен как <tt>void</tt>, то выражение результата не указывается. Если тип возвоащаемого значения не указан, то подразумевается что функция возвращает [[Переменные#Нетипированные (динамические) переменные|нетипированную переменную]]; если тип указан явно, то при возврате значения, тип результата выражения будет приведен к типу результата. Если типы неприводимы, то будет сгенерирована ошибка времени компиляции.<br />
<br />
Приведем примеры объявления функций с различными типами возвращаемых значений и прокомментируем каждый из случаев:<br />
<source lang="kpp"><br />
function F1(int x) { <br />
if (x > 5)<br />
return "hello world";<br />
else <br />
return x;<br />
}<br />
function string F2() { return 10; }<br />
function void F3() { return; }<br />
</source><br />
<br />
Первая функция возвращает переменную [[Переменные#Нетипированные (динамические) переменные|динамического типа]], которая может быть либо строкой, либо числом в зависимости от значения аргумента. Вторая функция возвращает строку, но в качестве выражения в операторе <tt>'''return'''</tt> указана константа типа <tt>[[Стандартные типы данных#Целые числа|int]]</tt>, поэтому будет выполнена операция приведения. Третья функция не возвращает результата, поэтому оператор <tt>'''return'''</tt> записан без выражения.<br />
<br />
== Локальные переменные ==<br />
<br />
Существуют несколько видов переменных, каждый из которых объявляется в своей области и действует в ее пределах. ''Глобальные'' переменные объявляются в контексте пакета (файла) и действуют в его пределах. Такие переменные могут быть доступны из любой функции или метода, объявленных в том же пакете. Переменные, объявленные в теле класса называются ''полями''. Их область применения ограничена методами этого класса и его потомков (более точно поведение задается с помощью спецификаторов доступа); за пределами класса такие переменные не видны.<br />
<br />
Самой узкой областью действия обладают локальные переменные. Они объявляются и действуют в пределах отдельной функции, или даже ее части. Локальные переменные служат для хранения промежуточных результатов рассчетов, хранения состояния локальных объектов и другой временной информации. При выходе контекста управления из области действия переменой (например при выходе из функции) она уничножается.<br />
<br />
=== Объявление ===<br />
<br />
Объявление локальных переменных ничем не отличается от объявления переменной в любой другой области. Переменные могут объявляться в любом месте функции или метода. Единственное, что нужно иметь в виду: переменная должна быть объявлена до места ее использования. Хорошей практикой является объявление переменных как можно ближе к месту их применения. Одними из существенных недостатков языка программирования Паскаль можно назвать отсутствие возможности объявления переменных прямо по ходу программы. Видимо, авторы Паскаля считали, что это дезорганизует программиста и делает его код менее читаемым. Однако, практика показывает, что при программировании достаточно больших процедур и функций, это требование только мешает. Программисту приходится "бегать" вперед-назад от текущего кода к блоку описания переменных и наоборот. При этом, вместо собственно решения проблемы, он занимается оформлением кода и разбором получившейся кучи локальных переменных, сваленных в одном месте. Это сильно отвлекает и в конечном счете ведет к появлению ошибок. Практика объявления переменных по мере использования помогает программисту сосредоточиться на текущем месте программы, не отвлекаясь от него и имея весь код "на виду". <br />
<br />
=== Область видимости ===<br />
<br />
Как уже было сказано выше, каждая переменная действует в пределах своей области видимости. Для локальных переменых, областью видимости является тот блок программы, в пределах которого она была объявлена. Она будет доступна в пределах всего блока, а так же во всех вложенных блоках. Если во вложенном блоке объявляется переменная с тем же именем что уже была объявлена ранее, то этому имени в коде программы будет сопостовляться внутренняя переменная. При изменении внутренней переменной значение внешней не меняется. За пределами блока переменная считается необъявленной, то есть ее использование в выражениях приведет к ошибке времени компиляции. <br />
<br />
Ниже приведен пример некоторой функции, в которой рассмотрены типовые ситуации, возникающие при работе с локальными переменными. В качестве упражнения, постарайтесь ответить на вопрос, какими будут значения переменных на момент вызова функции <tt>print()</tt> в каждом из случаев. Записывайте ваши ответы по ходу разбора листинга, а затем сравните их с [[Ответы к упражнению (область видимости)|контрольными ответами]].<br />
<br />
<source lang="kpp" line="1"><br />
function f(const int p) {<br />
var x = 1, k = 2;<br />
print("x = #{x}, k = #{k}\n"); //(1)<br />
<br />
{<br />
x = 5;<br />
var k = 3;<br />
print("x = #{x}, k = #{k}\n"); //(2)<br />
}<br />
print("x = #{x}, k = #{k}\n"); //(3)<br />
<br />
if (p > 5) {<br />
print("x = #{x}\n"); //(4)<br />
} else {<br />
var x = 10;<br />
var y = "hello";<br />
print("x = #{x}, #{y}\n"); //(5)<br />
x = 6;<br />
}<br />
print("x = #{x}\n"); //(6)<br />
print("#{y}"); //(7)<br />
}<br />
</source><br />
<br />
<br />
'''Примечание:''' В вышеприведенном коде, в строках 5-9 применен inline блок, который применяется для ''изоляции локальных переменных'' в некоторой области. Это может быть удобно в тех случаях, когда программист желает выполнить некоторые действия в пределах части функции, при том что все создаваемые временные переменные будут сконцентрированы в области этого блока. При выходе контекста выполнения программы за пределы этого блока, переменные будут освобождены при следующей операции сборки мусора (однако, повторно использовать идентификаторы можно сразу после блока).<br />
<br />
<!-- для того чтобы быть уверенным что используемые переменные будут своевременно освобождены. Примером такой ситуации может послужить работа с файлами. Предположим, что мы должны прочитать некоторый файл, после чего выполнить долгую и вычислительно сложную операцию по его обработке. Для обеспечения целостности существует требование, чтобы работа с файлом происходила в эксклюзивном режиме, то есть в один момент времени с файлом может работать только одно приложение, в то время как доступ других будет заблокирован. <br />
<br />
Если мы реализуем код вида:<br />
<source lang="kpp"><br />
function ProcessFile(const string filename = '/var/db/mydb') {<br />
var dbfile = OpenDB(filename);<br />
var db = ReadDB(myfile);<br />
ProcessDB(db);<br />
}<br />
</source><br />
...то произойдет следующее: сперва будет выполнена операция открытия файла и чтения его содержимого в функции <tt>ReadDB()</tt>. После этого, будет выполнена операция <tt>ProcessDB()</tt> в течение которой файл попрежнему будет открыт в эксклюзивном режиме и другие программы не смогут его использовать. На самом же деле, нашему приложению файл уже не нужен, так что его можно было бы и освободить. Для этого, мы должны применить дополнительный блок для изоляции переменной файла в пределах логического блока работы с файлом (который ограничен первыми двумя строками):<br />
<br />
<source lang="kpp"><br />
function ProcessFile(const string filename = '/var/db/mydb') {<br />
var db;<br />
{<br />
var dbfile = OpenDB(filename);<br />
var db = ReadDB(dbfile);<br />
}<br />
ProcessDB(db);<br />
}<br />
</source><br />
<br />
Теперь, переменная ''dbfile'' будет существовать только в пределах блока, соответственно, при выходе из него, переменная будет удалена и доступ к файлу будет открыт. --><br />
<br />
== Экспортирование функций ==<br />
<br />
Изначально, платформа и [[виртуальная машина Gide]] была разработана для свободного взаимодействия любого количества gide-совместимых языков программирования, которые могли бы свободно использоваться при написании программ. Причем, модуль написанный на одном языке программирования может импортировать другие модули, написанные на других языках. Одним из способов обеспечения совместимости языков является введение стандартного [[Gide IDL|языка описания интерфейсов]] модулей, который должны понимать языки, подразумевающие совместную работу в рамках системы.<br />
<br />
Написание совместимых программ накладывает определенные ограничения на именование идентификаторов классов и методов, поскольку в языках программирования имена методов как правило декорируются для обеспечения возможности перегрузки операторов и методов (см. ниже). Правила декорации имен в различных языках могут быть различными, соответственно пришлось бы их стандартизировать.<!--либо отказаться от декорации вообще, либо реализовывать механизм сопоставления декорированных и недекорированных имен. --><br />
<br />
В языке К++ введено специальное ключевое слово <tt>'''export'''</tt>, которое позволяет отменить декорацию имени для отдельно взятой функции. Зная имя функции, ее можно будет вызывать из внешнего кода (имеется в виду внешний по отношению к gide код, например код некоторого плагина).<br />
<br />
При отмене декорирования функции, накладываются определенные ограничения на перегрузку функций: попытка объявления двух экспортируемых методов с одинаковыми именами в рамках одного класса приведет к ошибке времени компиляции. Если экспортируется только один из этих методов, то это не является ошибкой.<br />
<br />
Экспортирование функций применяется при объявлении точек входа в программу. Для программ, такой точкой является функция <tt>main()</tt>: <br />
<source lang="kpp"><br />
export function void main() {<br />
/* тело функции */<br />
}<br />
</source><br />
<br />
После загрузки программы в память, происходит поиск функции с именем "main" которой передается управление.<br />
<br />
== Перегрузка функций и операторов ==<br />
<br />
<font color="red" size="5">'''Внимание!'''</font> В текущей (второй) версии компилятора, данная опция еще не реализована. Это связано с отменой декорированияя имени метода информацией о параметрах. В качестве временного решения для реализации некоторого подобия перегруженных методов можно применить код эмуляции:<br />
<br />
<source lang="kpp" line="1"><br />
class MyClass {<br />
public operator MyClass + (x) {<br />
switch (x.className) {<br />
case 'std/int': { /* MyClass + int */ }<br />
case 'std/string': { /* MyClass + string */ }<br />
default: { /* MyClass + (x as MyClass) */ }<br />
}<br />
}<br />
}<br />
</source><br />
<br />
<br />
;3: Объявляется оператор <tt>+</tt>, принимающий в качестве параметра динамическую переменную.<br />
;4: В коде оператора реализуется множественный выбор на основании класса переданного фактического параметра<br />
;5-7: Определяются возможные случаи для поддерживаемых типов. Первый будет вызываться, если в качестве параметра передана строковая переменная, второй — если это число. Выбор по умолчанию будет соответствовать общему случаю, когда передан неизвестный тип. Здесь можно либо попытаться выполнить операцию явного приведения типа к самому <tt>MyClass</tt>, либо к одному из поддерживаемых; либо сразу бросить исключение.<br />
<br />
<br />
<hr><br />
<br />
<br />
При разработаке интерфейсов классов, часто бывает необходимо предоставить возможность выполнения одних и тех же операций с различными типами исходных данных. Например, класс пользовательского хранилища данных должен обладать методами добавления (импортирования) элементов из стандартных хранилищ, вроде [[Стандартные типы данных#Массивы и списки|массивов и списков]]. Точно так же, он должен предоставлять интерфейс экспортирования данных в стандартные типы. Если интерфейс будет реализован с учетом только одного из типов, то теоретически программа все же сможет работать (поскольку, типы <tt>array</tt> и <tt>list</tt> являются взаимоприводимыми). На практике, эта операция может привести к значительным потерям производительности, связанным в основном с многократным копированием содержимого при операции приведения.<br />
<br />
Решение может заключаться в реализации нескольких методов для импортирования и экспортирования данных в определенных форматах:<br />
<source lang="kpp"><br />
class MyCollection {<br />
public:<br />
function Add(const x); //добавление одиночного элемента<br />
function AddArray(const array a); //добавление массива<br />
function AddList(const list l); //добавление списка<br />
function AddCSV(const string s, //добавление строки с разделителем<br />
const string separator = ",");<br />
function Insert(const x, const int pos = 0); //вставка одиночного элемента<br />
function InsertArray(const array a, const int pos = 0); //вставка массива<br />
function InsertList(const list l, const int pos = 0); //вставка списка<br />
function InsertCSV(const string s, //вставка строки с разделителем<br />
const int pos = 0, const string separator = ",");<br />
const function array ExportArray(); //экспортирование в массив<br />
const function list ExportList(); //экспортирование в список<br />
const function string ExportCSV(); //экспортирование в строку<br />
}<br />
</source><br />
<br />
Теперь, при использовании такого класса, программисту придется для каждой операции применять функцию, рассчитанную на данный тип параметров:<br />
<source lang="kpp"><br />
var col = new MyCollection;<br />
col.Add("hello world");<br />
col.AddArray([1, 2, 3]);<br />
var l = new list;<br />
foreach (1 .. 10) { |x| l.push(x); };<br />
col.AddList(l);<br />
col.AddCSV("волк,коза,капуста");<br />
//(код вставки и экспортирования пишется по аналогии)<br />
</source><br />
<br />
С одной стороны, вроде бы код довольно читаем и красив, но с другой, — есть одна маленькая неприятность. Допустим, программист написал некоторую функцию или даже целый класс, которые часто работают с инстанциями контейнера <tt>MyCollection</tt> и со списками, которые импортируются в контейнер. Предположим, что однажды было решено, для повышения быстродействия использовать массивы вместо списков. В коде интерфейсов это изменение может затронуть всего пару строк, в то время как код функции придется переписывать, заменяя все вызовы <tt>AddList()</tt> и <tt>InsertList()</tt> на соответствующие им вызовы: <tt>AddArray()</tt> и <tt>InsertArray()</tt>. Если в коде функции существует только одна сущность представленная списком, то достаточно сделать замену в тексте одной подстроки на другую. Но что если их много? Придется проходить код строка за строкой и проверять каждый вызов — благодатная почва для ошибок. Конечно, кому-то может показаться, что приведенный пример "притянут за уши", однако такая проблема на самом деле имеет место, просто довольно сложно придумать навскидку подходящую ситуацию.<br />
<br />
<br />
Как вы уже могли догадаться, наиболее рациональное решение заключается в использовании ''перегруженных функций''. Перегруженными, называются функции или методы, которые имеют одинаковые имена, но различные списки параметров. Вот примеры перегруженных функций:<br />
<source lang="kpp"><br />
function Hello(int x); //1<br />
function Hello(int x, real y); //2<br />
function Hello(string x = "the value"); //3<br />
function Hello(array a, list l, hash h); //4<br />
</source><br />
Все вышеприведенные функции могут быть с успехом объявлены в одной области: либо как глобальные функции, либо в пределах класса — как методы. Как видите, имена всех функций одинаковы, а отличаются они лишь списком параметров. Отличия могут быть как в количестве параметров, так и в их типах.<br />
<br />
При вызове такого метода, первым делом проверяется количество фактически передаваемых параметров и их типы. На основании этой информации компилятор решает, какой же из методов необходимо использовать в каждом конкретном случае. Если, к примеру, пользователь вызвал функцию как <tt>Hello(1)</tt>, то очевидно что имелся в виду метод номер 1. Вызов <tt>Hello(1, 2)</tt> будет соответствовать методу номер 2. Несмотря на то что тип второго фактического параметра это <tt>int</tt>, компилятор все равно разберется, поскольку это единственный из методов, принимающий два параметра. Конечно, при генерации вызова будет выполнена операция приведения типа для второго параметра к типу <tt>real</tt>. Наконец, вызов метода <tt>Hello()</tt> без параметров будет соответствовать методу номер 3, поскольку в описании присутствует инициализатор.<br />
<br />
Аналогичным образом перегружаются и операторы.<br />
<br />
=== Особенности применения ===<br />
<br />
При использовании перегрузки функций существует несколько ограничений, которые нужно иметь в виду при написании интерфейсов и программ. Перегружать функции и методы на основании типа их возвращаемого значения нельзя. Это невозможно по нескольким причинам. Во-первых, подобная практика привела бы к написанию плохого кода, который очень трудно отлаживать при возникновении каких либо проблем. Во-вторых, и это самое главное, существуют ситуации, когда компилятор не может решить какую из функций необходимо выбрать, например такое может случиться при использовании [[Переменные#Нетипированные (динамические) переменные|динамических переменных]]:<br />
<source lang="kpp"><br />
function int f() { return 1; }<br />
function string f() { return "hello"; }<br />
<br />
function caller() {<br />
var x; //динамическая переменная<br />
x = f(); //какую из функций вызвать?!<br />
}<br />
</source><br />
В приведенном примере выражение <tt>x = f();</tt> не может быть корректно разобрано, поскольку компилятору не на что равняться при выборе конкретной реализации функции. Даже если бы мы использовали явное приведение типов в выражении (<tt>x = f() as int</tt>), то компиляция все равно была бы неуспешной, поскольку с точки зрения компилятора эта операция должна выполняться уже после самого вызова функции.<br />
<br />
Та же проблема возникает и в случае передачи динамической переменной в перегруженную функцию:<br />
<source lang="kpp"><br />
function f(int x) { return x; }<br />
function f(string s) { return x; }<br />
<br />
function caller() {<br />
var x; //динамическая переменная<br />
f(x); //какую из функций вызвать?!<br />
}<br />
</source><br />
Написание подобного кода ведет к ошибке времени компиляции. Однако, такая проблема возникает только при одинаковом количестве неинициализированных параметров в перегруженных функциях. <br />
<br />
В случае с операторами, в неоднозначной ситуации будет генерироваться динамический код. Поскольку операторы имеют строго определенные списки параметров и как правило принимают всего один или два параметра, то это оправдано. В случае с функциями, пришлось бы генерировать динамический код, даже если 9 из 10 параметров статические, а 1 динамический (который все и портит). Ограничение на функции было введено намеренно, чтобы избежать большого количества динамического кода и как следствие — падения производительности. <br />
<br />
Разрешение вышеописанной ситуации для функций сводится к принудительной типизации переменной:<br />
<source lang="kpp"><br />
function caller() {<br />
var x;<br />
f(x to int);<br />
}<br />
</source><br />
<br />
=== Применение перегрузки в расширениях ===<br />
<br />
Еще одним аргументом в пользу перегруженных методов, является возможность их применения в [[Классы и объекты#Расширения|расширениях]]. Например, программист может расширить функциональность некотрого системного класса, добавляя возможность передачи своего класса в качестве аргумента методам. При этом, он добавляет метод, имеющий то же имя (но другой набор параметров), что и метод, существующий в расширяемом классе. <br />
<br />
При расширении сторонних классов следует быть особенно внимательным и не забывать, что правила декорирования имен методов в других языках могут быть различными, либо декорирование может не применяться вовсе. К примеру, в случае [[Стандартная библиотека Gide|стандартной библиотеки]], применять расширения можно смело, но нельзя их помечать как <tt>'''export'''</tt>.</div>Raw mathttp://man.deeptown.org/index.php/%D0%A2%D1%83%D1%82%D0%BE%D1%80%D0%B8%D0%B0%D0%BB%D1%8B_%D0%B8_HOWTOТуториалы и HOWTO2013-07-13T12:41:26Z<p>Raw mat: </p>
<hr />
<div>На этой странице размещаются документы, затрагивающие практические вопросы использования платформы диптауна, а так же пошаговые руководства, позволяющие научиться выполнять разнообразные операции, вроде импортирования новых моделей, написания скриптов и т. д.<br />
<br />
Если у вас есть идея или вы хотите поделиться своим опытом -- пожалуйста пишите свои HOWTO и помогите новичкам. Так же, не стоит забывать про [http://forum.deeptown.org форум], на котором можно вести обсуждение статей и задавать вопросы.<br />
<br />
При написании собственной статьи, указывайте пожалуйста свое авторство. Это можно сделать автоматически, с помощью последовательности символов '''<nowiki>--~~~~</nowiki>'''<br />
<br />
<br />
== Туториалы ==<br />
<br />
Здесь должны размещаться статьи, которые подразумевают изучение некоторой темы на некотором конкретном примере. Читатель, повторяя операции должен в итоге прийти к тем же результатам что и автор.<br />
<br />
* [[С чего начать|Первый запуск и советы новичкам]] --[[Участник:Korvin|Korvin]] 12:34, 27 апреля 2008 (EDT)<br />
* [[Тюнинг машины для достижения лучших результатов]] --[[Участник:Korvin|Korvin]] 12:34, 29 апреля 2008 (EDT)<br />
* [[Работа с флеш объектами на примере скрипта bonnet.kpp]] --[[Участник:Korvin|Korvin]] 12:34, 29 апреля 2008 (EDT)<br />
<br />
== HOWTO ==<br />
<br />
HOWTO ориентируются на более общий случай и описывают действия в более общем смысле, однако все равно должны отражать четкую последовательность действий, которая должна выполняться пользователем для достижения результата.<br />
<br />
* [[Импортирование моделей в Диптаун]] --[[Участник:Korvin|Korvin]] 12:34, 29 апреля 2008 (EDT)<br />
* [[Добавление текстуры для земли и ящиков]] --[[Участник:Stmf|Stmf]] 22:42, 21 июля 2008 (EDT)</div>Raw mathttp://man.deeptown.org/index.php/%D0%A0%D0%B0%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B0Рассылка2013-07-13T12:32:44Z<p>Raw mat: </p>
<hr />
<div>Для получения самой свежей информации о событиях, происходящих с Deeptown SDK, Вы можете подписаться на рассылку новостей.<br />
<br />
На данный момент эта рассылка предоставляется только по личному обращению. [[Обратная связь|Напишите нам]] письмо, в котором опишите, кто Вы и чем планируете заниматься - мы добавим Ваш адрес к рассылке. Это требуется для сбора информации о наших пользователях и формирования сообщества разработчиков для платформы Диптаун.<br />
<br />
Естественно, мы обещаем не распространять и тем более не продавать адреса наших подписчиков.</div>Raw mathttp://man.deeptown.org/index.php/%D0%A1_%D1%87%D0%B5%D0%B3%D0%BE_%D0%BD%D0%B0%D1%87%D0%B0%D1%82%D1%8CС чего начать2013-07-13T12:25:49Z<p>Raw mat: </p>
<hr />
<div>'''Внимание: Эта страница устарела. Некоторая информация может оказаться неактуальной'''<br />
<br />
== Где я? ==<br />
<br />
Если вы читаете эту страницу, то видимо уже установили себе Deeptown SDK и хотите что либо с ним сделать :)<br />
Если еще не установили, то вам сюда: [[Deeptown SDK]].<br />
<br />
Данная страница предназначена для того, чтобы дать самое общее представление о том, что же из себя представляет система Диптауна на данный момент и что с ней можно сделать интересного. Здесь мы постараемся (в свободной форме) описать возможные пути исследования и продемонстрировать некоторые вещи на примерах.<br />
<br />
То что здесь будет описано, по большей части преследует одну цель — дать человеку отправную точку и помочь сориентироваться в многообразии информации. Мы не в состоянии объяснить все, а можем лишь указать путь :) Поэтому — пробуйте, пытайтесь, изучайте систему и делитесь своим опытом на форуме :) Кроме того, эта wiki — так же к вашим услугам. Если вам есть что добавить или исправить — милости просим.<br />
<br />
== Что это? Как попасть в Диптаун? ==<br />
<br />
SDK это набор программных средств, включающий в себя саму платформу диптауна (aka "движок"), а так же набор разнообразных, сопутствующих программ, таких как компилятор языка К++. Короче говоря, это основа, на базе которой и будет строиться Диптаун. На данный момент, "того" Диптауна еще не существует. Точнее, существуют некоторые его части у наших архитекторов, но они еще не в публичном доступе. Так что, "попадать" пока еще некуда.<br />
<br />
Тем не менее, обычные пользователи могут попробовать использовать "все это" для того, чтобы оценить возможности системы и просто посмотреть "красивые картинки", которые скоро будут. Ниже будет описано, как заставить систему работать и посмотреть некоторые уже существующие демки.<br />
<br />
Люди, желающие поглубже изучить саму систему, найдут здесь полезную информацию о том, как совершаются типовые действия, вроде добавления объектов и их программирования. Более подробно это будет рассмотрено в соответствующей документации.<br />
<br />
== Первый запуск ==<br />
[[Изображение:Showto 1.png|thumb]]<br />
[[Изображение:Showto 2.png|thumb]]<br />
Для начала попробуем запустить все "как есть". Для этого нам понадобится собственно Deeptown SDK и архив с медиа файлами, который уже должен быть при вас, если вы внимательно читали инструкцию по установке. <br />
<br />
Попробуйте запустить файл deep.exe (если вы в windows) или /opt/deeptown/bin/deep в *nix. Будьте терпеливы, потому что в первый запуск система производит индексацию содержимого каталога Media (того самого) и создает в нем файл ".index", который используется для поиска файлов самой системой. При последующих запусках эта операция выполняться не будет (только в [[Индексация DISS|определенных случаях]]).<br />
<br />
Если все прошло успешно, то вы должны будете увидеть окошко рендеринга и, через некоторое время, простейшую демосцену, загружаемую по умолчанию. На данный момент это сцена "bonnet", содержащая в себе машину и кучку кубиков. Выглядеть это должно примерно как на картинках справа. <br />
<br />
Машиной можно ездить и сбивать кубики. Изначально они расположены справа и сзади от вас (конечно это все можно изменить!)<br />
<br />
=== Управление ===<br />
<br />
* Ctrl+Q — переключиться в режим курсора (управление окошком)<br />
* Двойное нажатие на заголовке внутреннего окна сворачивает его в заголовок (чтобы меньше места занимать)<br />
* W, S, A, D — движение камерой '''при зажатой кнопке Alt'''<br />
* Стрелки клавиатуры — управление машиной (попробуйте наехать на кубики)<br />
* Стрелка назад — тормозить (если едем назад — наоборот, давить кнопку вперед)<br />
* Кнопка "spawn object" интерфейса — создает в воздухе еще один кубик, где то неподалеку. Кубики создаются всегда случайным образом (и это тоже можно изменить!).<br />
* Кнопка "cleanup" — удаляет все созданные вами кубики (те которые были изначально — созданы другим путем, поэтому они останутся на месте)<br />
<br />
'''Примечания''': <br />
* Машина (а так же форма интерфейса) управляется скриптом /media/storage/media/scripts/bonnet.kpp<br />
* Переназначить клавиши управления можно редактируя файл /media/storage/etc/world/input.conf<br />
* Чтобы выйти из системы, надо либо нажать крестик на окне интерфейса, либо нажать Alt+Tab и закрыть окно рендеринга<br />
* Если вы обнаружили что то странное, либо программа попросту "совершила недопустимую операцию", то смело идите в багтрекер по адресу [http://bugs.deeptown.org http://bugs.deeptown.org] и оставляйте свое сообщение. <br />
* Есть еще и другие сцены, которые можно попробовать запустить. Для этого надо открыть файл /media/storage/etc/boot/services и раскомментировать одну из строчек client_wm:..., не забыв закомментировать строчку, где упоминается bonnet.scene!<br />
* Текущая версия модели ведет себя не очень хорошо, из за плохой настройки физических параметров. Для улучшения характеристик модели предусмотрен [[Тюнинг_машины_для_достижения_лучших_результатов|специальный туториал]]. Выполнив его вы научитесь править модели и получите хорошую модель машинки :)<br />
<br />
== Консоль ==<br />
<br />
Консоль Диптауна является стандартным средством взаимодействия с системой, которая позволяет (точнее будет позволять) всецело управлять системой и отдавать ей команды. На данный момент она реализована только в самых общих чертах (даже стандартных команд оболочки еще нет). В будущем она будет существенно расширена и дополнена. <br />
<br />
Подключение к консоли осуществляется по протоколу Telnet, любым из клиентов, поддерживающих этот протокол.<br />
<br />
Перед первым подключением необходимо создать пользователя Диптауна (не путать с пользователем операционной системы!). Для его создания необходимо запустить сценарий mkuser.pl, который находится в папке с медиаданными. У Вас будут запрошены имя пользователя, пароль, а также идентификаторы самого пользователя и его группы.<br />
<br />
Чтобы подключиться к консоли из ОС Windows надо:<br />
# зайти в меню Пуск<br />
# открыть диалог "выполнить" (или сразу нажать Win+R)<br />
# ввести туда <tt>telnet localhost 4830</tt> и нажать Enter<br />
<br />
Линуксоиды скорее всего и сами разберутся ;)<br />
<br />
Далее, можно попробовать запустить программы, имеющиеся по умолчанию. Например можно ввести команду /bin/hello.gbc и получить простенькое тестовое окно с известным сообщением. Если Вы сами написали консольную программу и откомпилировали её с помощью kpp, не забудьте добавить полученный gbc-файл в файловую систему, как описано [[Запуск_файла_kpp_из_консоли_Диптауна|здесь]]. Просьба не судить строго, ведь это не конечный продукт, а всего лишь тесты :)<br />
<br />
Если у Вас возникнет желание написать более толковое приложение — обращайтесь к нам и [http://forum.deeptown.org на форум].<br />
<br />
Выход из консоли осуществляется вводом команды exit.<br />
<br />
== Что еще можно посмотреть? ==<br />
<br />
Практически все "интересные" вещи сосредоточены в уже известной вам директории Media. Приведем здесь список основных поддиректорий с комментариями к ним.<br />
<br />
;Media/storage: Это корневая директория для внутренней ФС диптауна. Все внутренние пути указываются относительно нее. Скажем, если в файле материала указана строка texture <tt>/media/textures/grass.png</tt>, то это значит что реальный файл расположен по адресу <tt>Media/storage/media/textures/grass.png</tt>.<br />
;Media/storage/bin: Здесь будут располагаться исполняемые файлы скриптов.<br />
;Media/storage/boot: Директория системного загрузчика (ничего интересного).<br />
;Media/storage/etc: А здесь хранятся файлы настроек. Заглянуть рекомендуется настоятельно. По крайней мере поизучать. Попробуйте загрузить другую сцену вместо той что грузится по умолчанию. '''Примечание''': Все текстовые файлы в диптауне используют формат UNIX, когда строка переводится одним символом LF. Поэтому, в windows открывать их надо либо wordpad-ом (он правильно понимает переносы), либо другим редактором, поддерживающим такие файлы. Хорошо работает редактор в FAR Manager-е.<br />
;Media/storage/home: пока пустует. <br />
;Media/storage/media: Здесь тоже много чего интересного. Это основное хранилище различных медиа данных, имеющих отношение к собственно виртуальному пространству. Содержит такие поддиректории как:<br />
::* '''materials''' — файлы материалов OGRE. Определяют графические свойства поверхностей моделей, такие как текстуры, цвета, привязанные шейдеры и др.<br />
::* '''mein''' — медиафайлы для встроенного графического интерфейса, такие как скины и шрифты<br />
::* '''meshes''' — здесь хранятся меш файлы загружаемых моделей. Более подробно про это можно почитать в статье [[импорт моделей в диптаун]].<br />
::* '''models''' — а здесь хранятся сами модели. Модель это нечто большее, чем просто меш с натянутой текстурой. Файл модели описывает также понятия, относящиеся к другим представлениям объекта, а не только к графическому. Например, там указывается масса модели, момент ее инерции и много других параметров. Смотри так же [[понятие объекта в пространстве]].<br />
::* '''scenes''' — если кратко, то сцена — это совокупность объектов, расположенных в некоторой области виртуального пространства. В этой директории хранятся файлы локальных сцен.<br />
::* '''scripts''' — самое интересное :) Здесь расположены скрипты, задающие поведение отдельных объектов. Поскольку диптаун изначально создавался как интерактивная среда, то каждый объект, находящийся в виртуальном пространстве, может быть "живым" и "разумным". С объектом ассоциируется некоторый скрипт, который программирует его действия и управляет его поведением. Например, скрипт bonnet.kpp реализует интерфейс к той самой машине и позволяет пользователю (то есть нам с вами) управлять ей. Скрипт park.kpp создает интерфейс управления анимацией для сцены park, и сам создает эту анимацию. Все скрипты написаны на нашем языке [[K++]].<br />
::* '''shaders''' — хранилище для шейдеров. Думаю что это такое, объяснять не требуется.<br />
::* '''textures''' — ну и здесь наверное все понятно.<br />
<br />
== Что еще можно сделать? ==<br />
<br />
Это зависит, в первую очередь, от вашего желания и способностей, а так же от имеющихся навыков. <br />
<br />
=== Программистам ===<br />
<br />
Если вы знакомы с такими языками программирования как Паскаль, Си++ или Java, то вам не составит труда изучить, или по крайней мере разобраться, в языке [[K++]]. Он очень похож на эти языки.<br />
<br />
<s>Попробуйте например, модифицировать скрипт bonnet.kpp так, чтобы создаваемые кубики располагались не случайным образом а один над другим. Главное поставить высоту побольше, чтобы кубики не пытались телефрагить друг друга.</s><br />
<br />
=== Моделерам ===<br />
<br />
Если вы умеете моделировать объекты в 3D пакетах, таких как 3D Studio, Maya или Blender, и желаете создать свои модели, то вам стоит обратиться в раздел [[Разработка моделей]]. Там в скором времени появится информация о том, каких правил следует придерживаться при создании моделей, а так же детально будет расписан процесс конвертирования моделей в формат Диптауна.<br />
<br />
В настоящее время, мы испытываем большой дефицит людей этой профессии, так что если вас заинтересовал проект — смело обращайтесь [http://forum.deeptown.org на форум]. Мы постараемся оказать вам всестороннуюю помощь и поддержку.<br />
<br />
=== Всем ===<br />
<br />
Как вы понимаете, написание пространной и качественной документации — длительный и трудоемкий процесс, отнимающий наше время и силы. Несмотря на то, что мы стараемся поддерживать документацию на уровне, это не всегда возможно. Поэтому, если вы желаете поделиться собственным опытом в изучении Диптауна или написать HOWTO на определенную тему — пробуйте. Эта wiki к вашим услугам :) Собственно, она и создавалась для этих целей. В ближайшее время, мы постараемся написать серию HOWTO, затрагивающих основные вопросы, вроде скриптования объектов и создания собственных сцен. Ссылки на них будут размещаться [[Туториалы и HOWTO|здесь]]. Присоединяйтесь :)<br />
<br />
Вы можете помочь проекту, даже не будучи программистом или дизайнером. Для этого достаточно просто быть грамотным человеком. Если на страницах этой wiki вы встретите неточности, орфографические, либо пунктуационные ошибки, или просто заметите дефиc там, где должно стоять тире — исправьте, пожалуйста, текст так, как вы считаете нужным. Это можно сделать с помощью вкладки "Править" вверху каждой страницы, либо с помощью соответствующей ссылки в каждом заголовке.<br />
<br />
Если вы сомневаетесь относительно верности вашего исправления, тогда вы можете написать об этом на странице обсуждения. Не забудьте только указать место в тексте, где, по вашему мнению, находится ошибка и ваш вариант написания. Ссылка на страницу обсуждения находится так же, вверху каждой страницы.</div>Raw mathttp://man.deeptown.org/index.php/%D0%A2%D0%B8%D0%BF%D1%8B_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BE%D0%B2Типы операторов2013-07-13T12:23:03Z<p>Raw mat: </p>
<hr />
<div>Операторы отличаются от обычных функций тем, что имеют определенные ограничения по количеству<br />
аргументов, а также некоторое ожидаемое поведение. Например, оператор + (сложение), принимающий<br />
более чем один аргумент, выглядел бы глупо. Кроме того, вряд ли кто-то ждет от оператора сложения<br />
изменение объекта, для которого он вызван, или аргумента.<br />
<br />
Рассмотрим различные типы операторов более подробно.<br />
<br />
== Арифметические ==<br />
<br />
К этому типу относятся следующие операторы:<br />
* <tt>+</tt>, <tt>-</tt>, <tt>*</tt>, <tt>/</tt>, <tt>%</tt><br />
* бинарный сдвиг: <tt><<</tt>, <tt>>></tt><br />
* бинарная логика: <tt>|</tt> (или), <tt>&</tt> (и), <tt>^</tt> (исключающее или), <tt>~</tt> (не)<br />
* инкремент и декремент: <tt>++</tt>, <tt>--</tt><br />
<br />
Все арифметические операторы, за исключением бираного НЕ, инкремента и декремента, принимают<br />
один параметр. Они не должны менять исходный объект и объект-параметр; результат операции<br />
должен быть возвращен в виде вновь созданного объекта. Таким образом, и оператор и его аргумент<br />
должны быть объявлены константными.<br />
<br />
В качестве примера приведем реализацию некоторых операторов для класса двумерного вектора:<br />
<br />
<source lang="kpp"><br />
class Vector2 {<br />
var real m_x, real m_y; // координаты<br />
public:<br />
property real x read m_x write m_x;<br />
property real y read m_y write m_y;<br />
<br />
// конструктор для упрощения создания объекта<br />
constructor create(const real X, const real Y)<br />
{<br />
var self = new Vector2;<br />
self.m_x = X;<br />
self.m_y = Y;<br />
return self;<br />
}<br />
<br />
// операторы сложения и скалярного произведения векторов:<br />
const operator Vector2 + (const Vector2 arg)<br />
{<br />
// возвращаем новый объект в качестве результата!<br />
return Vector2.create(x + arg.x, y + arg.y);<br />
}<br />
<br />
const operator real * (const Vector2 arg)<br />
{<br />
// скалярное произведение<br />
return x * arg.x + y * arg.y;<br />
}<br />
};<br />
</source><br />
<br />
<br />
== Операторы сравнения ==<br />
<br />
Существует 6 операторов сравнения: "меньше", "больше", "меньше или равно", "больше или равно", "равно" и "не равно" -<br />
соответственно, <tt><</tt>, <tt>></tt>, <tt><=</tt>, <tt>>=</tt>, <tt>==</tt>, <tt>!=</tt>.<br />
<br />
Все эти операторы также должны принимать один параметр, не должны изменять объект и параметр, а кроме того, все они<br />
должны возвращать в качестве результата логическое значение - true или false.<br />
<br />
'''Примечание:''' в стандартной библиотеке языка K++ объявлен класс-[[примесь]] [[Comparable]], который упрощает реализацию<br />
этих операторов.<br />
<br />
Достаточно унаследовать свой класс от [[Comparable]] и определить в классе метод <tt>compare</tt>, который принимает один<br />
аргумент и возвращает значение<br />
* меньшее нуля, если объект меньше аргумента;<br />
* равное нулю, если объект равен аргументу;<br />
* большее нуля, если объект больше аргумента.<br />
<br />
Все остальные операторы, а также метод <tt>between</tt>, определены в [[Comparable]].<br />
<br />
== Операторы присваивания ==<br />
<br />
Оператор присваивания <tt>=</tt> - это оператор, который копирует значение своего аргумента в текущий объект.<br />
<br />
Кроме того, каждому арифметическому оператору соответствует оператор присваивания, имя которого составляется из<br />
имени математического оператора плюс символ "=" (например, <tt>+=</tt>). Такие операторы делают соответствующее<br />
математическое действие с текущим объектом.<br />
<br />
Операторы присваивания изменяют текущий объект, но не изменяют аргумент. Все они, кроме <tt>~=</tt>, <tt>--=</tt><br />
и <tt>++=</tt>, принимают ровно один аргумент.<br />
<br />
Операторы присваивания должны возвращать текущий объект в качестве результата.<br />
<br />
Операторы <tt>--=</tt> и <tt>++=</tt> соответствуют префиксному инкременту и декременту.<br />
<br />
Для того же класса <tt>Vector2</tt> из предыдущего примера:<br />
<br />
<source lang="kpp"><br />
operator Vector2 += (const Vector2 arg)<br />
{<br />
x += arg.x;<br />
y += arg.y;<br />
return this;<br />
}<br />
</source><br />
<br />
<br />
== Операторы приведения типов ==<br />
<br />
Часто бывает так, что одни и те же объекты могут быть представлены различными способами. Например, число 5 может<br />
быть представлено как целочисленное 5, вещественное 5.0 или в виде строки "5".<br />
<br />
Операторы приведения типов позволяют преобразовывать объекты из одних классов в другие.<br />
<br />
Имя такого оператора совпадает с именем класса, в который производится преобразование; оператор не принимает<br />
параметров и не должен менять текущий объект.<br />
<br />
При объявлении оператора приведения типа, тип результата функции указывать не следует.<br />
<br />
Продолжим пример с двумерным вектором. В некоторых задачах бывает целесообразно определить преобразование вектора<br />
в вещественное число, возвращающее длину вектора.<br />
Это можно сделать следующим образом:<br />
<br />
<source lang="kpp"><br />
const operator real ()<br />
{<br />
return sqrt( x * x + y * y );<br />
}<br />
<br />
// В коде:<br />
var v = Vector2.create(3.0, 4.0);<br />
var len = v as real; // => 5.0<br />
</source><br />
<br />
<br />
== Оператор индексного доступа ==<br />
<br />
Существует два оператора - <tt>[]</tt> и <tt>[]=</tt> - оператор индексного чтения и оператор индексной записи.<br />
<br />
Оператор индексного чтения принимает не менее одного параметра (возможно более одного), не меняет объект и параметры<br />
и должен возвращать значение - значение элемента по указанным индексам.<br />
<br />
Оператор индексной записи принимает не менее двух параметров. Последний параметр всегда соответствует значению, которое<br />
должно быть записано. Оператор изменяет объект, но не изменяет свои параметры.<br />
<br />
Для двумерного вектора может быть удобным проиндексировать координаты. В этом случае можно определить следующие операторы:<br />
<br />
<source lang="kpp"><br />
const operator real [] (const int idx)<br />
{<br />
switch(idx) {<br />
case 0: return x;<br />
case 1: return y;<br />
default: throw ERangeError.create('index is out of range');<br />
}<br />
}<br />
<br />
const operator Vector2 []= (const int idx, const real value)<br />
{<br />
switch(idx) {<br />
case 0: x = value;<br />
case 1: y = value;<br />
default: throw ERangeError.create('index is out of range');<br />
}<br />
}<br />
<br />
// В коде:<br />
var v = Vector2.create(0, 0);<br />
v[1] = 5; // => (0, 5)<br />
var r = v[0]; // => 0<br />
</source><br />
<br />
<br />
== Оператор вызова функции ==<br />
<br />
Специальный оператор <tt>call</tt> используется для того, чтобы объект можно было бы использовать как функцию.<br />
Нет никаких ограничений на параметры и поведение этого оператора.<br />
<br />
Пример:<br />
<br />
<source lang="kpp"><br />
class GreetingPrinter {<br />
public operator call (const string who)<br />
{<br />
puts("Hello, #{who}!");<br />
}<br />
}<br />
<br />
// Использование:<br />
var greeting = new GreetingPrinter;<br />
greeting("Peter"); // выведет "Hello, Peter!"<br />
<br />
function f(const b)<br />
{<br />
b("Vova");<br />
}<br />
<br />
f() { |x| puts("Hi, #{x}!"); }; // выведет "Hi, Vova!"<br />
f(new GreetingPrinter); // выведет "Hello, Vova!"<br />
</source></div>Raw mathttp://man.deeptown.org/index.php/%D0%A1%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D1%8B%D0%B5_%D1%82%D0%B8%D0%BF%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85Стандартные типы данных2013-07-13T12:18:30Z<p>Raw mat: </p>
<hr />
<div>В этой главе будут рассмотрены базовые типы данных, применяющиеся в языке К++. Большинство из них объявлены в стандартной библиотеке Gide, однако некоторые, например интервалы, описываются в системной библиотеке самого языка К++. Еще раз напомним Читателю, что в языке К++, стандартные типы данных не являются встроенными. Конечно, компилятор опирается на них при генерации кода, но это совершенно не означает, что их нужно воспринимать как что-то единожды определенное и неизменное. Как уже было показано в книге, как с точки зрения компилятора так и самой виртуальной машины, эти классы ничем не отличаются от обычных, пользовательских классов, когда дело касается их использования на высоком уровне. Если забежать вперед, то можно отметить, что на низком уровне они реализованы на языке C++ (из соображений производительности) и их интерфейсы представлены в стандартной библиотеке. Тем не менее, существует возможность их дополнения программистом-пользователем, что и было проделано в главах, посвященных [[Классы и объекты#Расширения|расширениям]]. Разумеется, от этих классов возможно наследовать собственные классы, точно так же, как и от любых других. В этой главе мы рассмотрим стандартные типы данных с точки зрения их применения и укажем некоторые свойства которые не были упомянуты в ходе повествования.<br />
<br />
== Целые числа ==<br />
<br />
Основой всей арифметики являются числа. Для представления чисел в программах применяются классы, объекты которых выступают как хранилища значений. Существует несколько классов целых чисел, различающихся по длине охватываемого ими диапазона и по возможности указания знака: <br />
<br />
Для педставления целых чисел применяется класс <tt>int</tt>, умеющий хранить числа в диапазоне от -9223372036854775808 до 9223372036854775807.<br />
<br />
Класс поддерживает все возможные арифметические операции, а так же имеет операторы преобразования в строку и из строки. Однако, следует быть осторожным и не полагаться полностью на автоматическое приведение типов. В некоторых случаях, можно получить не те результаты, что ожидает программист. Например:<br />
<source lang=kpp><br />
var i = 1;<br />
var j = "2";<br />
puts(i + j);<br />
puts(j + i);<br />
</source><br />
<br />
В приведенном выше примере, объявлены две переменных: целочисленная ''i'' и переменная ''j'', содержащая строку "2". При вычислении первой суммы происходит следующее: компилятор "видит", что необходимо посчитать сумму переменных ''i'' и ''j'', которые для нас являются представлениями чисел 1 и 2. Но с точки зрения компилятора, это всего лишь две инстанции классов <tt>int</tt> и <tt>string</tt> соответственно.<br />
<br />
Разбор выражения производится слева-направо, так же как его считает человек. Компилятор пытается найти в классе <tt>int</tt> оператор <tt>+</tt> с типом второго операнда, то есть <tt>string</tt>. Такого оператора в классе нет. Тогда компилятор смотрит, существует ли в классе <tt>string</tt> [[Типы операторов#Операторы приведения типов|оператор приведения]] к типу <tt>int</tt>, либо к другому типу, для которого в классе <tt>int</tt> объявлен оператор <tt>+</tt>. Такой оператор находится, соответственно компилятор генерирует вызов оператора приведения для переменной ''j'', а затем уже выполняется сам оператор <tt>+</tt>. Поэтому, сложение происходит численно, так как и ожидалось. Результат суммы будет соответствовать числу 3:<br />
<source lang=kpp><br />
i + j = 1 + ("2" as int) = 1 + 2 = 3<br />
</source><br />
<br />
Во втором случае, все происходит с точностью наоборот: первым операндом является строка, то есть инстанция класса <tt>string</tt>. В этом классе так же объявлен оператор <tt>+</tt>, но принимающий в качестве второго операнда строку. Этот оператор используется для конкатенации двух строк, при которой вторая строка дописывается в конец первой, то есть:<br />
<source lang=kpp><br />
var s1 = "hello";<br />
var s2 = "world";<br />
puts(s1 + s2); //результат: "hello" + "world" = "helloworld"<br />
</source><br />
<br />
В нашей сумме, вторым операндом является инстанция класса <tt>int</tt>, которая по вышеописанным правилам, будет приведена к типу <tt>string</tt>. Таким образом, операция суммы будет приведена к операции конкатенации строк "2" и "1", то есть результат выражения будет "21":<br />
<source lang=kpp><br />
j + i = "2" + (1 as string) = "2" + "1" = "21"<br />
</source><br />
<br />
<br />
Приведенная проблема особенно опасна при использовании [[Переменные#Нетипированные (динамические) переменные|динамических переменных]], поскольку в одном случае может произойти одна операция, а в другом другая. Поэтому, использовать динамические переменные следует очень осторожно. Если у вас нет уверенности относительно типа передаваемой переменной, лучше подстраховаться и произвести операцию явного преобразования к интересующему вас типу.<br />
<br />
== Числа с плавающей точкой ==<br />
<br />
Для проведения сложных математических рассчетов, одних целых чисел недостаточно. Для представления действительных чисел, или чисел с плавающей точкой (запятой), как их называют в информатике, в стандартной библиотеке создан класс <tt>real</tt>, который на низком уровне представлен типом двойной точности (double). <br />
<br />
В отличие от класса <tt>string</tt>, класс <tt>real</tt> корректно обрабатывается в сочетании с целочисленными классами. Например, следующие выражения дадут одинаковые результаты:<br />
<source lang=kpp><br />
var i = 3 + 0.1415<br />
var j = 0.1415 + 3<br />
</source><br />
<br />
В результате, обе переменных будут иметь тип <tt>real</tt> и значение <!--примерно -->3,1415. Это достигается тем, что в классе <tt>int</tt> есть специальные версии арифметических операторов, которые принимают в качестве параметра объекты класса <tt>real</tt>.<br />
<br />
== Логический тип ==<br />
<br />
В некоторых случаях в программе бывает необходимо сохранять логическое значение. Это может потребоваться в алгоритмах, где такое значение используется в качестве флага, либо в свойствах объектов, которые подразумевают наличие или отсутствие некоторого признака. Это осуществляется с помощью булевых переменных и псевдотипа <tt>bool</tt>. Переменные булевого типа могут иметь только два значения: <tt>true</tt>, соответствующее истине и <tt>false</tt>, соответствующее лжи.<br />
<br />
К переменным булевого типа могут приводиться значения более сложных выражений, например когда такое выражение находится в условии цикла. Правила преобразования значений выражений были описаны при рассмотрении [[Основные синтаксические конструкции#Оператор if|условного оператора <tt>'''if'''</tt>]].<br />
<br />
'''Примечание:''' псевдотипом <tt>bool</tt> назван потому, что он не является классом в полной мере. Это абстракция, которая была введена в язык К++ для преодоления некоторых сложностей при проектировании языка. Переменные такого класса обрабатываются на уровне самого К++; с точки зрения Gide такого класса не существует (вернее, существуют специальные объекты <tt>'''@true'''</tt> и <tt>'''@false'''</tt>, но это тема, требующая отдельного обсуждения).<br />
<br />
Существует несколько ограничений, которые важно понимать. Первое ограничение заключается в том, что от этого типа нельзя наследовать собственные классы. Второе ограничение проявляется в том, что переменные типа <tt>bool</tt> обрабрабатываются особым образом. Если в выражении присутствует переменная булевого типа, то операторы отношения работают немного по другому. Например, следующее выражение будет трактовано как истина:<br />
<source lang=kpp><br />
if (0 == true)<br />
//...<br />
</source><br />
<br />
Условие сработает потому что 0 представляет собой действительный, существующий объект, стало быть он истинен (об этом уже было написано). Вот другой пример, иллюстрирующий ту же проблему:<br />
<source lang=kpp><br />
var MyClass c = new MyClass;<br />
if (c == true)<br />
//...<br />
</source><br />
<br />
Подобное условие так же сработает (будет считаться истинным), поскольку оператор <tt>==</tt> в данном случае обрабаытвается самим компилятором. Конечно, приведенные примеры являются довольно странными с точки зрения обычного кода, но их все же следует иметь в виду при написании программ.<br />
<br />
== Строки ==<br />
<br />
Для хранения и обработки строковых данных используется класс <tt>string</tt>. Он объявлен в [[Стандартная библиотека Gide|стандартной библиотеке Gide]] и дополнен с помощью [[Классы и объекты#Расширения|расширений]] в системной библиотеке К++. <br />
<br />
В языке К++ предусмотрены целых три синтаксиса для объявления строковых констант, которые в конечном счете представляются одним и тем же классом <tt>string</tt>.<br />
<br />
Строки, заключенные в двойные кавычки обрабатывают все escape последовательности, а так же позволяют использовать т. н. вставки, или подстановки — кусочки кода, которые вставляются непосредственно в тело строки и при работе программы заменяются на значение, возвращаемое этим кодом (см. ниже). Обрабатываются следующие последовательности:<br />
::{|<br />
! Последовательность || Значение<br />
|-<br />
| <tt>'''\n'''</tt> || перенос строки<br />
|-<br />
| <tt>'''\r'''</tt> || возврат каретки<br />
|-<br />
| <tt>'''\t'''</tt> || символ табуляции<br />
|-<br />
| <tt>'''\"'''</tt> || символ двойной кавычки<br />
|-<br />
| <tt>'''\\'''</tt> || символ обратного слеша<br />
|-<br />
| <tt>'''#{ }'''</tt> || подстановка выражения<br />
|}<br />
<br />
При этом, при обработке строки, найденные последовательности заменяются на соответствующий им управляющий символ. Например, последовательность <tt>'''\n'''</tt> будет заменена на один символ перевода строки (ASCII код 13).<br />
<br />
Подстановки это очень удобный способ совмещать в одном выражении строку которую необходимо вывести и выражения, значение которых необходимо добавить к выводу. Получается что подстановки это что-то среднее между простой строкой и строкой для форматируемого вывода. Подстановка начинается с пары символов <tt>'''#{'''</tt>, которые должны идти друг за другом, без пробелов. То что идет дальше, воспринимается компилятором как выражение, как если бы оно было записано просто в коде, вне строки. Завершает подстановку соответствующая ей закрывающая фигурная скобка.<br />
<br />
Приведем пример использования подстановок:<br />
<source lang=kpp><br />
var s1 = "сумма чисел 2 и 3 равна #{2 + 3}.";<br />
print("s1 = #{s1}\n");<br />
</source><br />
<br />
При разборе выражения компилятор преобразовывает данное выражение в сумму, представляющую из себя последовательность строковых констант и выражений:<br />
<source lang=kpp><br />
var s1 = "сумма чисел 2 и 3 равна " + (2 + 3) + ".";<br />
print("s1 = " + (s1) + "\n");<br />
</source><br />
<br />
В результате выполнения этого кода, в выводе появится текст (курсор будет переведен на следующую строку):<br />
s1 = сумма чисел 2 и 3 равна 5.<br />
<br />
<br />
Подстановки даже можно делать вложенными, например так:<br />
<!--<source lang=kpp>--><br />
var x = [ 1, 2, 3, 4, 5 ];<br />
var s = "some weird stuff: #{ x.join(', ') { |idx,elem| "#{idx} = #{elem}"; } }\n";<br />
<!--</source>--><br />
<br />
Если требуется работать со строкой, которая содержит escape последовательности (или подстановки), которые не требуется обрабатывать то следует применять строки, заключенные в одинарные кавычки:<br />
<source lang=kpp><br />
var s1 = 'в таких строках \n \r \t и #{подстановки} не обрабатываются';<br />
</source><br />
Исключением из правила являются последовательности '''<tt><nowiki>\'</nowiki></tt>''' и <tt>'''\\'''</tt>.<br />
<br />
Третьим способом объявить строку является применение синтаксиса кратких, "однословных" строк. Такие строки начинаются со знака двоеточия, следом за которым могут идти те же символы, что возможны при написании идентификаторов, то есть: латинские буквы, цифры и знак подчеркивания, притом что первой после двоиточия обязательно идет либо буква, либо подчеркивание. Такие строки могут быть особенно полезны при записи ключей хеша (как будет показано ниже), либо просто в тех местах где в качестве строки передается одно слово:<br />
<br />
<source lang=kpp><br />
var h = { :sunday = 1, :monday = 2, :tuesday = 3 };<br />
var x = my_function(:hello);<br />
</source><br />
<br />
<br />
Так же, класс <tt>string</tt> предоставляет пользователю широкий спектр различных методов, которые могут использоваться для работы со строками.<br />
<br />
=== Длина строки ===<br />
<br />
Для определения длины строки используется метод <tt>length()</tt>, который возвращает число символов в строке. Так же, существует метод <tt>empty()</tt> который возвращает истину если строка пуста и ложь в противном случае.<br />
<br />
=== Выделение подстрок ===<br />
<br />
Для выделения из исходной строки подстрок, разделенных символом или несколькими символвами разделителями, используется метод <tt>split()</tt>. В качестве параметра, этот метод принимает строку, либо [[Введение, или краткий обзор#Регулярные выражения|регулярное выражение]], соответствующие разделителю. Возвращает метод уже массив из полученных подстрок.<br />
<br />
В качестве примера приведем два способа обработки URL строки и выделения из нее отдельных частей:<br />
<source lang=kpp><br />
var url = "http://example.com?key1=value1&key2=value2&key3=value3";<br />
var qm = url.find("?");<br />
var args = url.substr(qm, url.length());<br />
var pairs = args.split('&');<br />
var options = new hash;<br />
pairs.each() { |pair| <br />
var kv = pair.split('='); <br />
options[kv[0]] = kv[1]; <br />
};<br />
</source><br />
<br />
С помощью метода <tt>find()</tt> мы находим позицию символа вопроса в исходной строке, который разделяет адрес на собственно адрес ресурса и на строку параметров запроса. Строка параметров состоит из множества пар ''значение''<tt>=</tt>''результат'', отделенных друг от друга символом амперсанда. Выделив подстроку запроса, мы разделяем ее на подстроки с помощью метода <tt>split()</tt>, указывая амперсанд в качестве разделителя. Полученный массив подстрок мы сохраняем в переменной ''pairs''. Теперь, мы заводим [[Стандартные типы данных#Хеши|хеш]] ''options'' в который будем записывать параметры запроса, по мере его обработки. Выполняя перебор по всем парам, мы опять разделяем их на две части, на этот раз по символу равенства. Последним действием, полученная пара значение-результат сохраняется в хеше. <br />
<br />
Несмотря на то, что вышеприведенный код работает, он является не очень эффективным. В процессе его работы создается много промежуточных переменных и может задействоваться большое количество памяти (при длинных строках запросов). При задачах обработки строк выгодно использовать регулярные выражения. Они позволяют относительно легко производить обработку строк; выполнять поиск и замену по некоторому шаблону. Приведем другой, более элегантный способ решения той же самой задачи, но уже с использованием регулярных выражений:<br />
<source lang=kpp><br />
var options = new hash;<br />
if (url =~ `\?(.*)$`) {<br />
//разделяем строку на пары значение=результат<br />
var pairs = $[1].value.split('&');<br />
//обрабатываем пары по очереди<br />
pairs.each() { |pair|<br />
if (pair =~ `^([\w\d]+)=([\w\d]+)$`)<br />
options[$[1].value] = $[2].value;<br />
};<br />
}<br />
</source><br />
<br />
Оператор <tt>=~</tt> служит для связи исходной строки с регулярным выражением. В результате, в специальный массив <tt>$[]</tt> будут помещены все совпадения. Элемент с индексом 0 будет соответствовать всей совпавшей подстроке, в остальных элементах будут находиться совпадения, соответствующие подвыражениям в скобках. <br />
<br />
'''Примечание 1:''' элементы спецмассива <tt>$[]</tt> представляют собой не сами подстроки, а объекты-совпадения, класса <tt>regexp_match</tt>. В этих объектах содержится дополнительная информация о совпадении, например: <br />
* индексы левой и правой границы (свойства <tt>left</tt> и <tt>right</tt>)<br />
* длина подстроки (свойство <tt>length</tt>)<br />
* и, естественно, сама подстрока (свойство <tt>value</tt>)<br />
<br />
Получить доступ к подстроке так же можно, выполнив явное приведение объекта совпадения к типу <tt>string</tt>. Таким образом, следующие строки функционально эквивалентны:<br />
<source lang=kpp><br />
var substr1 = $[1].value;<br />
var substr2 = $[1] as string;<br />
</source><br />
<br />
'''Примечание 2:''' в принципе, можно было и не объявлять отдельную переменную ''pairs''. В книге это было сделано для большей читаемости кода, однако на практике может применяться и более лаконичная форма, которая, тем не менее, остается довольно удобной к восприятию:<br />
<source lang=kpp><br />
if (url =~ `\?(.*)$`) {<br />
$[1].value.split('&').each() { |pair|<br />
if (pair =~ `^([\w\d]+)=([\w\d]+)$`)<br />
options[$[1].value] = $[2].value;<br />
};<br />
}<br />
</source><br />
<br />
При желании, можно еще больше упростить код, используя версию оператора <tt>[]</tt>, принимающую регулярное выражение в качестве параметра и возвращающее соответствующую ему подстроку:<br />
<source lang=kpp><br />
url[`\?(.*)$`].value.split('&').each() { |pair|<br />
if (pair =~ `^([\w\d]+)=([\w\d]+)$`)<br />
options[$[1].value] = $[2].value;<br />
};<br />
</source><br />
<br />
Однако, в этом случае, при отсутствии требуемой подстроки в исходной переменной ''url'', будет сгенерировано исключение и это следует корректно обрабатывать.<br />
<br />
=== Объединение строк (конкатенация) ===<br />
<br />
Конкатенация строк осуществляется с помощью обычных арифметических операторов (при этом, приоритеты операций остаются теми же). В зависимости от оператора, действие будет производиться над самим объектом или над его копией:<br />
<source lang=kpp><br />
var s1 = "hello";<br />
var s2 = "world";<br />
var s3 = s1 + ' ' + s2; // s3 = "hello world", s1 и s2 те же<br />
s1 = (s3 += "!"); // s1 = s3 = "hello world!"<br />
s2 *= 2; // s2 = worldworld<br />
s3 = s1 + '!' * 2; // s3 = "hello world!!!"<br />
</source><br />
<br />
Обратите внимание на две последние строки. Оператор <tt>*</tt>, выполняемый над строкой действительно "умножает" ее, повторяя заданное количество раз.<br />
<br />
=== Операторы индексного доступа ===<br />
<br />
Для доступа к отдельным символам строки может использоваться оператор индексного доступа <tt>[]</tt>. При этом, строка представляется как [[Стандартные типы данных#Массивы и списки|массив]], где каждый элемент соответствует '''номеру символа''' в unicode:<br />
<source lang=kpp><br />
var str = "hello world";<br />
var ord = str[0]; // ord = 104<br />
</source><br />
<br />
Можно совершать и обратную операцию, а именно присваивать по некоторому индексу числовое значение. Тогда в строке будет стоять символ, соответствующий указанному числу:<br />
<source lang=kpp><br />
str[0] = 72; // str = "Hello world"<br />
str[6] = 'W'[0]; // str = "Hello World"<br />
</source><br />
<br />
Существует дополнительный синтаксис, специально предназначенный для получения номера некоторого символа. Он осуществляется записью символа диез ("решетка"), с последующим добавлением исследуемого символа. Таким образом, вторую строку из предыдущего примера можно записать так:<br />
<source lang=kpp><br />
str[6] = #W; // str = "Hello World"<br />
</source><br />
<br />
Этот синтаксис можно применять даже для управляющих символов вроде '''\n''' и даже для любого символа Unicode:<br />
<source lang=kpp><br />
var n = #\n; // n будет содержать число 13<br />
var f = #Ф; // код, соответствующий русской заглавной букве Ф<br />
</source><br />
<br />
Если же требуется получить подстроку исходной строки на основании индексов символов, нужно применять [[Стандартные типы данных#Интервалы|интервалы]]:<br />
<source lang=kpp><br />
var s2 = str[0 .. 4]; // s2 = "Hello"<br />
</source><br />
<br />
Возможно так же использовать оператор индексного доступа для присвоения значения отдельным подстрокам. Это осуществляется с помощью оператора <tt>[]=</tt>:<br />
<source lang=kpp><br />
var greeting = "Welcome to the real world, Neo.";<br />
greeting['real world'] = 'matrix'; // Welcome to the matrix, Neo.<br />
greeting[15 .. 20] = 'Deeptown'; // Welcome to the Deeptown, Neo.<br />
greeting[-1] = #!; // Welcome to the Deeptown, Neo!<br />
greeting[`\sNeo.$`] = 'Leonid!'; // Welcome to the Deeptown, Leonid!<br />
</source><br />
<br />
В примере выше, применены четыре способа индексного доступа для изменения строки, соответствующие четырем различным операторам (точнее, четырем версиям перегруженного оператора). В первом случае используется доступ по подстроке. При этом подстрока будет "вырезана" из строки, а на ее место будет вставлена присваиваемая строка. Таким образом, эта операция не зависит от размера искомой и заменяемой подстрок (если заменяемая строка длиннее, исходная строка будет "раздвинута").<br />
<br />
Во втором случае осуществляется доступ по интервалу. Символы с индексами в диапазоне 15-20 соответствуют слову "matrix". Замена опять же производится по правилу "вырезания" подстроки с последующим "склеиванием".<br />
<br />
В третьем случае, используются отрицательные индексы, для указания того, что отсчитывать символы нужно с конца строки. Так, -1 соответствует последнему символу, -2 второму с конца и т. д.<br />
<br />
Наконец, в последней замене применяется [[Введение, или краткий обзор#Регулярные выражения|регулярное выражение]], для поиска подстроки.<br />
<br />
'''Примечание:''' Методы замены через оператор <tt>[]=</tt> на деле являются обертками к соответствующим реализациям метода <tt>replace_all()</tt>. При этом, будут производиться замены всех включений подстроки в исходную строку (разумеется, кроме случаев ин тервала и одиночного индекса). Если вы хотите заменить только одно включение, — используйте методы <tt>replace()</tt>.<br />
<br />
== Интервалы == <br />
<br />
В некоторых случаях бывает необходимо передать в качестве параметра не отдельное значение а диапазон. Для указания диапазона обычно достаточно указать только его границы. Это может осуществляться с помощью класса <tt>interval</tt>. К++ предоставляет специальный синтаксис для указания интервалов с помощью оператора "<tt>..</tt>"; как уже было показано выше, это может применяться для выборки подстрок, соответствующих заданному диапазону индексов, либо подмассивов по тому же принципу:<br />
<source lang=kpp><br />
var s = "abcdef"[0..2]; // "abc"<br />
var a = [1, 2, 3, 4][1..-1]; // [2, 3, 4]<br />
</source><br />
<br />
Вообще, интервалы могут быть не только численными. В качестве объектов, формирующих интервал, могут выступать любые два объекта, имеющие одинаковые типы:<br />
<source lang=kpp><br />
var left = MyClass.Create(5), right = MyClass.Create(10);<br />
var i = interval.create(left, right);<br />
var alpahbet = 'a' .. 'z';<br />
</source><br />
<br />
Интервалы могут использоваться как виртуальные массивы, то есть к ним можно обращаться как к массиву, запрашивая некоторый элемент по индексу. И он будет возвращен, как будто действительно хранится в массиве (на сама деле, он создается в момент обращения по некоторому известному закону):<br />
<source lang=kpp><br />
var numbers = 1 .. 100;<br />
var alpahbet = 'a' .. 'z';<br />
var x = numbers[50]; // x = 1 + (50-1) = 50.<br />
var y = alphabet[10]; // y = (#a + (10-1)).char = 'k'<br />
</source><br />
<br />
Преимуществом такого подхода является то, что не нужно хранить весь массив в памяти. Зная закон изменения элементов, можно получить любой элемент, зная базу (одну из границ) и индекс элемента.<br />
<br />
Подобно массивам, интервалы так же обладают методом <tt>each()</tt>, позволяющем проходить по всему массиву, выполняя некоторый блок с параметром текущего элемента массива:<br />
<source lang=kpp><br />
var s = 0; //здесь будет сумма<br />
var numbers = 1 .. 100;<br />
numbers.each() { |x| s += x; };<br />
</source><br />
<br />
Для выяснения принадлежности некоторого объекта к интервалу могут применяться метод <tt>contains()</tt> или соответствующий ему оператор <tt>'''in'''</tt>:<br />
<source lang=kpp><br />
var numbers = 1 .. 100;<br />
var lowercase = 'a' .. 'z';<br />
var x = numbers.contains(75) ? "yes" : "no"; //x = "yes"<br />
var y = 'X' in lowercase ? "yes" : "no"; //y = "no"<br />
</source><br />
<br />
Для создания интервалов на базе собственных классов необходимо перегрузить конструктор, а так же соответствующие методы, своими реализациями.<br />
<br />
== Массивы и списки ==<br />
<br />
Для хранения наборов объектов применяются массивы и списки. И те и другие являются контейнерами, то есть, их объекты способны хранить в себе другие объекты и обладают соответствующими методами для добавления в них элементов и их извлечения. Разница между массивом и списком заключается в том, что массив хранит элементы в одной цельной области памяти, в то время как список организует их в виде цепочки связанных друг с другом элементов:<br />
<br />
[[Изображение:Array_vs_list.png|center]]<br />
<br />
У каждого из этих контейнеров есть свои преимущества и недостатки. Постараемся рассмотреть основные операции, осуществляемые с контейнерами и выяснить, в каких случаях предпочтительнее использовать массив, а в каких список.<br />
<br />
=== Доступ к данным ===<br />
<br />
Массивы обладают высокой скоростью доступа к информации, поскольку все данные находятся в одном месте и структурированы. То есть, доступ есть сразу ко всем элементам, поэтому не приходится двигаться вдоль массива для поиска нужного элемента. Списки работают несколко по другому. Для того, чтобы обратиться к некоторому элементу списка, необходимо пройти всю цепочку от начала до требуемого элемента (см. рисунок выше). Поэтому, списки значительно уступают по скорости доступа массивам. В случаях, когда содержимое контейнера меняется относительно редко, но присутствует большое количество обращений к данным, следует применять массивы.<br />
<br />
=== Добавление элементов ===<br />
<br />
Рассмотрим операцию добавления элементов в список. Предположим, что нам надо добавить элемент N в список, между вторым и третьим его элементами. Для выполнения этой операции, нам необходимо всего лишь "разорвать" цепочку в нужном месте и изменить указатель у предыдущего элемента X<sub>2</sub> так, чтобы он указывал на новый элемент N. Для того, чтобы продолжить цепочку, элемент N должен указывать на следующий элемент списка, то есть на X<sub>3</sub>:<br />
[[Изображение:List insert.png|center]]<br />
<br />
Операция добавления к началу или к хвосту списка так же не составляет труда, и сводится все к той же работе с указателями. Удаление элементов производится обратным образом: элемент удаляется, а указатель предыдущего элемента меняется так, чтобы он указывал на последующий за удаляемым элемент.<br />
<br />
В случае массивов все усложняется. Как уже было сказано выше, массивы хранят элементы в монолитных областях памяти, следовательно, чтобы добавить или удалить элемент, приходится задействовать весь массив. Если места в текущей области памяти, занятой массивом недостаточно, чтобы вместить еще один элемент, то произойдет выделение нового блока памяти, с последующим копированием всех существующих элементов массисва в новый блок. <br />
<br />
Операция выделения производится "с запасом", то есть, размер нового блока будет больше, чем размер занимаемый всеми элементами, причем зависимость тут экспоненциальная: чем больеше будет добавляться элементов в массив, тем больше будет размер выделяемого блока памяти. Это делается для того, чтобы минимизировать риск полного копирования. Тем не менее, такое может случиться (а при операции вставки — случается обязательно), что в итоге может привести к значительной задержке.<br />
<br />
Рассмотрим тот же случай, что и в случае со списком, а именно, операцию вставки нового элемента, между вторым и третьим элементами массива. Для выполнения этой операции создается новый массив, с размером, достаточным для помещения всех предыдущих элементов, а так же нового элемента. Далее, происходит копирование всех элементов на соответствующие места:<br />
[[Изображение:Array insert.png|center]]<br />
<br />
Это случай неудачного стечения обстоятельств, при котором происходит копирование большого количества информации, что естественным образом сказывается на призводительности всей операции. Из этого можно сделать вывод, что массивы следует применять тогда, когда операции вставки или добавления элементов происходят редко. В случаях, когда необходимо организовывать стеки или очереди, где обращение по номеру элемента не требуется, наилучшим решением будет использование списков.<br />
<br />
=== Применение ===<br />
<br />
Разобравшись с философией работы массивов и списков, поняв разницу между ними и определившись с областями применения тех и других, мы переходим непосредственно к практике. Массивы и списки в языке К++ представляются классами <tt>array</tt> и <tt>list</tt> соответственно. Оба класса имеют очень похожие наборы методов, так что дважды изучать одно и то же не придется.<br />
<br />
Объявление массива реализуется с помощью специального синтаксиса, встроенного в язык К++. Это может осуществляться как в инициализаторе переменной, так и в любом другом выражении, вплоть до фактического параметра в коде вызова функции, или даже для указания значения по умолчанию для него. <br />
<br />
Для того, чтобы объявить массив, необходимо перечислить его элементы, заключив их в квадратные скобки и отделив друг от друга запятой:<br />
<source lang=kpp><br />
var my_array = [1, 2, 3];<br />
</source><br />
<br />
В качестве элементов массива могут быть указаны любые объекты: числа, строки, экземпляры классов, блоки и даже вложенные массивы с хешами — массиву все равно что хранить, поскольку все что он делает, это хранит ссылки на объекты:<br />
<source lang=kpp><br />
var myobject = new MyClass;<br />
var object_dump = [1, 3.14, 'hello', myobject, {|x| return x + 1;}, <br />
{:a => 1, :b => 2 }, [1,2,3], true, 10 .. 20];<br />
</source><br />
<br />
Можно сказать, что массив представляет собой реализацию абстрактного упорядоченного хранилища данных с возможностью произвольного доступа.<br />
<br />
Специального синтаксиса для создания списков нет, поскольку большинство задач которые решают списки не предполагают инициализации значениями. Если же это все таки потребовалось, на помощь придут массивы. Вот небольшой пример того как можно занести в список значения из массива, создаваемого компилятором автоматически:<br />
<source lang=kpp><br />
var my_list = new list;<br />
[1, 2, 3].each() { |x| my_list.push(x); };<br />
</source><br />
<br />
В этом примере показываются сразу два важных приема, которые являются одними из самых частых по использованию (если вы внимательно читали книгу, то наверняка это заметили). Метод <tt>each()</tt> используется для перебора всего содержимого коллекции (массива, списка или интервала) и вызова некоторого блока, передавая ему на каждой итерации значение текущего элемента. Таким образом, в случае нашего массива, блок будет вызыван три раза, с параметрами, соответственно, 1, 2 и 3. Метод <tt>push()</tt> присутствует как в массивах, так и в списках, и служит для добавления элемента в конец коллекции. Здесь он применяется для добавления текущего элемента массива в список. Получается, что по выполнении этой процедуры, список будет содержать в себе все элементы, присутствующие в массиве (в том же порядке).<br />
<br />
Существуют еще несколько методов, похожих на метод <tt>each()</tt>, но отличающихся передаваемыми в блок параметрами. Например <tt>each_index()</tt> вызывает блок, передавая ему текущий индекс, а метод <tt>each_pair()</tt> передает ему текущий индекс и элемент. За неимением индекса, списки два последних метода не реализуют. Переделаем немного предыдущий пример и покажем использование этих методов:<br />
<source lang=kpp><br />
var my_array = [1, 2, 3];<br />
var my_list = new list;<br />
my_array.each() { |x| my_list.push(x); };<br />
<br />
my_list.each() { |x| print("#{x}\n"); };<br />
my_array.each_pair() { |i, e| print("my_array[#{i}] = #{e}\n"); };<br />
</source><br />
<br />
<br />
Иногда бывает необходимо поместить элементы массива или списка в строку. Например, чтобы вывести их пользователю или, возможно, записать в файл некоторые настройки, подразумевающие список значений. Это может быть сделано с помощью метода <tt>join()</tt> который возвращает строку с элементами, перемежая их некоторой строкой-разделителем. Разделитель может быть передан в качестве параметра; по умолчанию это строка ", " (запятая с пробелом):<br />
<br />
<source lang=kpp><br />
var my_array = [1, 2, 3];<br />
var my_list = new list;<br />
my_array.each() { |x| my_list.push(x); };<br />
<br />
var s_list = '[' + my_list.join() + ']'; // slist = "[1, 2, 3]"<br />
//дальнейшая работа с s_list (например, вывод на экран)<br />
</source><br />
<br />
Это простой пример использования метода <tt>join</tt>, при котором он используется для создания текстового представления содержимого списка. Для удобства и указания границ, добавляются квадратные скобки. Вот другой пример использования метода, на этот раз, для сохранения некоторого списка ресурсов которые используются приложением:<br />
<br />
<source lang=kpp><br />
//подготавливаем список опций<br />
var search_dirs = new list;<br />
search_dirs.push('http://example.com/db/');<br />
search_dirs.push('ftp://example.org/db/');<br />
search_dirs.push('diss:/media/storage/~user1/');<br />
<br />
//открываем поток файла настроек<br />
var config = stream.open('diss:/etc/sample.conf', stream.create | stream.write);<br />
config.write('dbpath=' + search_dirs.join(';')); //записываем строку конфигурации<br />
</source><br />
<br />
Как мы видим, сначала происходит заполнение списка значениями URL ресурсов, а затем полученный список записывается в ''поток''. Класс потока это абстракция, предоставляемая ядром Диптауна для доступа к различным ресурсам. Все что необходимо сделать чтобы использовать потоки, это создать инстанцию класса <tt>stream</tt> с момощью конструктора <tt>create()</tt>, указав требуемый URL потока и задать режим доступа к ресурсу (с помощью констант класса). При этом, на пользовательском уровне, требуется только писать в поток и читать из него; пользователь не должен беспокоиться, каким образом будет происходить реальная работа с данными на низком уровне — этим занимается ядро. Если же ядро не поддерживает конкретный тип потока — будет сгенерировано исключение.<br />
<br />
Для последуюезго разбора полученной строки, можно применять уже известный нам метод <tt>split()</tt> и регулярные выражения.<br />
<br />
В заключение, приведем две реализации кода, вычисляющего первую сотню простых чисел. Одну реализацию мы напишем с использованием списков, а другую с помощью массивов. В нижеприведенном коде активно применяются циклы, так что, для лучшего понимания материала, Читателю предлагается забежать вперед и [[Основные синтаксические конструкции#Циклы|ознакомиться]] с ними. Итак, первая реализация:<br />
<source lang=kpp line=1><br />
function GetPrimes() {<br />
var primes = new list;<br />
var x = 1;<br />
while (primes.size() < 100) {<br />
x++; //текущий кандидат<br />
var is_prime = true;<br />
for (var p = primes.begin(); p != primes.end(); ++p)<br />
if (x % p.object == 0) {<br />
is_prime = false;<br />
break;<br />
}<br />
if (is_prime) {<br />
primes.push(x); //помещаем число в список<br />
print("the #{primes.size()} prime number is #{x}\n");<br />
}<br />
}<br />
}<br />
</source><br />
<br />
Алгоритм поиска такой: берем число и начинаем вычислять остаток от его деления на все элементы нашего списка, то есть, выполняем попытку разложения числа на простые сомножители. Если число делится без остатка хотя бы на один из сомножителей (условие в строке 9), то число не является простым. Следовательно проверять его дальше не имеет смысла. Мы прекращаем цикл с помощью оператора <tt>'''break'''</tt> и сбрасываем флаг ''is_prime''. Если после перебора всех множителей, флаг остался установленным, то это значит, что текущее число (переменная ''x'') является и вправду простым: его мы помещаем в конец списка. Так продолжается до тех пор, пока в списке не окажится 100 элементов. Для проверки размера списка (условие [[Основные синтаксические конструкции#Цикл while|цикла <tt>'''while'''</tt>]] в строке 4) применяется метод <tt>size()</tt>.<br />
<br />
Обратите внимание на то, каким образом реализуется перебор значений списка. Для этой операции применяется ''итератор''. Итератор — это специальный класс, объекты которого используются в качестве указателей на элементы контейнеров. Как правило, итераторы имеют методы для перемещения по контейнеру (операторы <tt>++</tt> и <tt>--</tt>) и некоторое свойство, позволяющее обратиться к текущему элементу, на который указывает итератор. В случае класса <tt>list_iterator</tt>, это свойство <tt>object</tt>.<br />
<br />
Для перебора списка применяется [[Основные синтаксические конструкции#Цикл for|цикл <tt>'''for'''</tt>]]. В инициализаторе цикла была создана управляющая переменная ''p'', которая и является инстанцией итератора (класса <tt>list_iterator</tt>). Список имеет специальный метод <tt>begin()</tt>, который создает инстанцию итератора и устанавливает ее на свое начало. <br />
<br />
Метод <tt>end()</tt> возвращает значение специального итератора, который всегда указывает на конец списка. Его нельзя двигать, и служит он только для одной цели — для проверки граничных условий. Важно понимать, что под концом списка понимается не последний его элемент, а именно конец — нечто, следующее за конечным элементом. Таким образом, условие в цикле <tt>'''for'''</tt> проверяет, есть ли еще элементы "справа" от итератора ''p''. Если условие истинно, значит итератор находится где то в середине списка и можно продолжать итерации; если ложно — значит предыдущий элемент был последним и больше элементов в списке нет.<br />
<br />
Вот второй способ реализации того же участка кода, но как уже говорилось выше, с помощью массива:<br />
<source lang=kpp line=1><br />
function GetPrimes() {<br />
var primes = new array;<br />
primes.resize(100); //задаем размер массива<br />
var x = 1;<br />
var count = 0; //текущее число найденных простых<br />
while (count < 100) {<br />
x++; //текущий кандидат<br />
var is_prime = true;<br />
for (var i = 0; i < count; ++i)<br />
if (x % primes[i] == 0) {<br />
is_prime = false;<br />
break;<br />
}<br />
if (is_prime) {<br />
primes[count++] = x; //помещаем число в массив<br />
print("the #{count} prime number is #{x}\n");<br />
}<br />
}<br />
}<br />
</source><br />
<br />
Поскольку мы заранее знаем размер массива, который нам может потребоваться (100 элементов), мы можем выделить всю необходимую память одним махом. Это будет намного быстрее, чем выделять память по мере итераций, ведь копирование элементов массива в новое место будет произведено всего один раз. Для изменения размера массива применяется метод <tt>resize()</tt>. В качестве аргумента метод принимает число — размер массива, который мы желаем получить. Если желаемый размер меньше текущего, то массив будет урезан до нового размера (элементы, оказавшиеся "за бортом", будут выброшены). Если больше — массив будет расширен, причем все старые элементы останутся невредимыми на своих местах, а новые примут значение <tt>'''null'''</tt>.<br />
<br />
Ввиду того, что размер массива был задан заранее, проверять конечное условие по количеству элементов нельзя (оно уже равно 100). Поэтому, была введена новая переменная ''count'', в которой мы будем хранить текущее количество найденных простых чисел, а поскольку размещаем мы их строго последовательно, то и индекс последнего найденного простого числа всегда будет известен (какой?).<br />
<br />
При работе с массивами никакие итераторы нам не нужны — у нас есть индексы. Для доступа к некоторому элементу массива нам надо знать его индекс. Сама операция получения значения элемента, осуществляется через оператор индексного доступа <tt>[]</tt>. То есть, в нашем случае, <tt>primes[0]</tt> будет соответствовать первому элементу массива, <tt>primes[1]</tt> — второму, и т. д. (не забывайте, что индексация производится с нуля).<br />
<br />
Сам алгоритм поиска чисел, практически один-в-один повторяет алгоритм, описанный в предыдущем примере, так что подробно мы его описывать не будем. В качестве упраждения на внимательность, вы можете попытаться найти все отличия. <br />
<br />
Давайте лучше попробуем проанализировать оба решения и выяснить, какое из решений более оптимально для поставленной задачи. Для того, чтобы ответить на этот вопрос, необходимо прикинуть общее количество операций добавления элементов в массив и сравнить с количеством операций доступа к массиву, или операций выборки. <br />
<br />
В нашем алгоритме, мы видим следующее: на каждое число-кандидат, мы вынуждены пробегать все элементы коллекции и выполнять над ними некоторые действия. Поскольку, выборка элементов происходит строго последовательно, особой разницы между массивом и списком тут нет. Операция добавления элементов также происходит строго в конец коллекции и довольно редко. В целом, учитывая произведенные оптимизации (вроде предустановки размера массива), можно заключить что сложности алгоритмов примерно равны. <br />
<br />
Не забывайте, что это связано с жестко заданными условиями работы алгоритма! Представьте, если бы нам требовалось реализовать алгоритм, выбирающий элементы из коллекции не последовательно, а некоторым случайным, или почти случайным образом. В случае списка, итераторы нам уже не помогли бы. Пришлось бы опять прибегать к операции последовательного прохождения цепочки для отыскания нужного элемента. В таких условиях, по скорости массив был бы далеко впереди списка. Чуть изменим условия — и все может перевернуться вверх дном. Предположим, что реализуется алгоритм, при котором элементы добавляются не в конец, а в середину коллекции, да еще заранее не известно, какой может быть размер коллекции (так что невыгодно применять <tt>resize()</tt>). В таких условиях, уже список будет выигрывать у массива.<br />
<br />
Получается, что от используемого алгоритма (а так же от четкости поставленных условий), во многом зависит то, насколько эффективен будет тот или иной контейнер. Вот почему при проектировании программ, требуется тщательным образом подходить к вопросу анализа вычислительной сложности алгоритмов и выбирать более подходящие средства для их реализации. Но как всегда, идеальных решений не бывает — выбор будет лежать где то посередине между ресурсоемкостью и производительностью.<br />
<br />
== Хеши ==<br />
<br />
Хеши являются еще одной формой контейнеров. Подобно массивам и спискам, они могут хранить в себе другие объекты, однако они отличаются и от тех и от других. Для доступа к элементу списка, необходимо использовать итераторы, в то время как для ссылки на элемент массива применяются индексы — числа, которые соответствуют номеру элемента в массиве. <br />
<br />
В этом отношении, хеши похожи на массивы, однако, для доступа к элементу используются не индексы, а совершенно произвольные объекты; или если перефразировать наоборот, то: для доступа к некоторому элементу хеша применяются индексы, которые могут быть представлены любым объектом: числом, строкой либо экземпляром пользовательского класса.<br />
<br />
Хеши обычно применяются там, где требуется организовать связь нескольких объектов. При этом, первому объекту-индексу, который в хешах называется ''ключом'', ставится в соответствие другой объект — ''значение''. Хеши хранят множество таких пар ключ-значение и, подобно спискам и массивам, предоставляют удобные методы для работы со своим содержимым.<br />
<br />
Чаще всего, хеши применяются для связи текстовых строк или для организации ''ассоциативных массивов'', у которых в качестве индекса применяется строка. Приведем несколько примеров применения хешей. Допустим, мы хотим поставить в соответствие названию дня недели его порядковый номер. То есть, строке "понедельник" должно соответствовать число 1, "вторнику" — 2 и так далее. Конечно, эту задачу можно решить и традиционным способом, например с помощью массивов:<br />
<source lang=kpp><br />
const weekdays = ['понедельник', 'вторник', 'среда', 'четверг',<br />
'пятница', 'суббота', 'воскресенье'];<br />
<br />
function int WeekDay2DayNr(const string weekday) {<br />
var result; //результат (динамическая переменная)<br />
weekdays.each_pair() { |nr, day| <br />
if (day == weekday) { <br />
result = nr + 1; <br />
break; <br />
}<br />
};<br />
if (result) //если переменной присвоено значение<br />
return result; //возвращаем его<br />
else //иначе, сообщаем об ошибке<br />
throw e_invalid_call.create('указанная строка не является днем недели');<br />
}<br />
</source><br />
<br />
В этом примере, мы заводим массив ''weekdays'', в который помещаем названия всех дней недели. <br />
<br />
В функции <tt>WeekDay2DayNr()</tt> мы перебираем все элементы массива и сравниваем их с контрольной строкой. Если происходит совпадение, то мы сохраняем текущее значение индекса (''nr'') в динамическую переменную ''result'' и прерываем цикл с помощью оператора <tt>'''break'''</tt>. <br />
<br />
Далее, с помощью условного оператора проверяется, было ли переменной ''result'' присвоено некоторое значение. Если было, то оно возвращается как результат функции; если нет (то есть, оно по прежнему равно <tt>'''null'''</tt>) — генерируется [[Идеология языка#Понятие исключения|исключение]].<br />
<br />
Данная функция будет исправно работать, однако в реальных условиях, при большом количестве элементов массива, операция поиска перебором может стать очень медленной. В таком случае, наилучшим решением, будет использование хешей. Перепишем предыдущую функцию так, чтобы она использовала хеш вместо массива: <br />
<source lang=kpp><br />
const weekdays = {'понедельник' => 1, 'вторник' => 2, 'среда' => 3, <br />
'четверг' => 4, 'пятница' => 5, 'суббота' => 6, <br />
'воскресенье' => 7};<br />
<br />
function int WeekDay2DayNr(const string day) {<br />
try {<br />
return weekdays[day];<br />
} catch (e_range_error e) {<br />
throw e_invalid_call.create('указанная строка не является днем недели');<br />
}<br />
}<br />
</source><br />
<br />
Посмотрите, насколько проще стал код. А главное, он стал намного быстрее! Хеши позволяют значительно повысить скорость доступа к информации за счет того, что вместо обычного перебора, применяются альтернативные методы. Если говорить кратко, то происходит операция "перемешивания" ключа, которая осуществляется с помощью [http://ru.wikipedia.org/wiki/Хеш-функция хеш-функции], в результате которой получается некоторое значение — хеш ключа. Это значение уже используется для выборки искомого объекта. Скорость операции возрастает за счет того, что от медленной операции сравнения ключей, мы переходим к быстрой операции сравнения хешей. Хеши изначально проектируются так, чтобы их было легко сравнивать; в то же время, они должны быть различными для разных значний ключа. Более подробно, про хеши и хеширование, можно [http://ru.wikipedia.org/wiki/Хеш-таблица почитать на Википедии].<br />
<br />
Для объявления хеш-таблицы ''weekdays'' применяется специальный синтаксис, встроенный в язык К++: необходимо в фигурных скобрах перечислить пары ключ-значение, отделяя их запятой. Для связи ключа и значения в паре, применяется оператор соответствия (<tt>=></tt>). Подобно массивам, объекты хеш-таблиц могут объявляться где угодно в коде, будь то инициализатор переменной, либо фактический параметр в коде вызова функции. В нашем случае, объект создается в инициализаторе [[константы]], которая будет иметь тип <tt>hash</tt>.<br />
<br />
Разумеется, для заполнения хеша можно применять и обычную операцию создания объекта, с последующим вызовом методов <tt>insert()</tt> для добавления информации в таблицу:<br />
<source lang=kpp><br />
var my_config = new hash;<br />
my_config.insert('verbosity', 3);<br />
my_config.insert('user name', 'nobody');<br />
my_config.insert('password', 'y&5#3Eff_');<br />
//...<br />
</source> <br />
<br />
Для заполнения хеша можно применять так же оператор индексного доступа:<br />
<source lang=kpp><br />
my_config[:path] = 'diss:/etc/myconf';<br />
</source> <br />
<br />
Между вызовом метода <tt>insert()</tt> и использованием оператора <tt>[]=</tt> есть небольшая разница. Она заключается в том, что случае, если в хеше уже присутствует пара значений с тем же ключом, то метод <tt>insert()</tt> сгенерирует [[Идеология языка#Понятие исключения|исключение]], в то время как оператор <tt>[]=</tt> заменит старое значение на новое. Существует так же метод <tt>replace()</tt>, который ведет себя подобно вышеозначенному оператору:<br />
<source lang=kpp><br />
my_config[:enabled] = 'true';<br />
my_config.replace('enabled', 'true');<br />
</source> <br />
<br />
<br />
Хеши, подобно массивам и спискам, обладают набором методов для перебора их содержимого, однако смысл некоторых методов немного отличается от своих аналогов в массивах:<br />
<source lang=kpp><br />
weekdays.each() { |day, nr| print("key: #{day}, value: #{nr}\n"); };<br />
var keys = weekdays.keys();<br />
keys.each() { |key| println(key); };<br />
weekdays.values().each() { |value| print(value as string + ' '); };<br />
</source><br />
<br />
В этом примере используются три различных метода, которые вызывают блок с парой параметров ключ-значение (метод <tt>each()</tt>), либо возвращают массивы ключей и значений (методы <tt>keys()</tt> и <tt>values()</tt> соответственно). Во второй и третьей строках, показывается способ отображения всех ключей хеша, с помощью метода <tt>keys()</tt> и промежуточной переменной. В последней строке вызовы методов совмещены в одной конструкции. Для наглядности приведем вывод, каким он должен был бы быть, при выполнении этого кода (подразумевается использование хеша ''weekdays'' из предыдущих примеров):<br />
<br />
key: понедельник, value: 1<br />
key: вторник, value: 2<br />
key: среда, value: 3<br />
key: четверг, value: 4<br />
key: пятница, value: 5<br />
key: суббота, value: 6<br />
key: воскресенье, value: 7<br />
понедельник<br />
вторник<br />
среда<br />
четверг<br />
пятница<br />
суббота<br />
воскресенье<br />
1 2 3 4 5 6 7<br />
<br />
'''Примечание 1:''' Здесь мы перечислили выводимые значения по порядку. Однако, на практике такого не случается. За счет операции перемешивания, порядок следования элементов теряется. Поэтому, если требуется сохранить порядок, необходимо либо где-то записывать очредность ключей, либо сортировать их при обработке. Например, код обработки элементов хеша в порядке очередности ключей может выглядеть примерно так:<br />
<source lang=kpp><br />
my_hash.keys().sort().each() { |key| print("key: #{key}, value: #{my_hash[key]}\n"); };<br />
</source><br />
<br />
'''Примечание 2:''' Как уже было сказано выше, хеши позволяют использовать объекты любого типа как в качестве ключа, так и в качестве значения. Для того, чтобы можно было указывать в качестве ключа объект пользовательского типа, он должен иметь реализацию метода <tt>hash()</tt>, не принимающего параметров и возвращающего объект класса <tt>int</tt>. Причем, требуется этот метод требуется экспортировать из модуля, для того чтобы стандартная библиотека смогла его найти. Дополнительно, класс объектов ключа должен предоставлять операторы сравнения и присвоения (<tt>==</tt> и <tt>=</tt>). На объекты, содержащиеся в значениях, никаких ограничений не накладывается. <br />
<br />
Таким образом, реализация класса ключей должена выглядеть примерно так:<br />
<source lang=kpp><br />
class MyClass {<br />
public:<br />
export const function int hash() {<br />
/* здесь идет код вычисления хеша */<br />
}<br />
<br />
const operator bool == (const MyClass x) {<br />
/* код сравнения инстанций */ <br />
}<br />
<br />
operator MyClass = (const MyClass src) {<br />
/* код присвоения */ <br />
return this;<br />
}<br />
/* другие методы */<br />
}<br />
</source><br />
<br />
== Указатели ==</div>Raw mathttp://man.deeptown.org/index.php/%D0%A2%D1%8E%D0%BD%D0%B8%D0%BD%D0%B3_%D0%BC%D0%B0%D1%88%D0%B8%D0%BD%D1%8B_%D0%B4%D0%BB%D1%8F_%D0%B4%D0%BE%D1%81%D1%82%D0%B8%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BB%D1%83%D1%87%D1%88%D0%B8%D1%85_%D1%80%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%D0%BE%D0%B2Тюнинг машины для достижения лучших результатов2013-07-13T12:16:42Z<p>Raw mat: </p>
<hr />
<div>'''Внимание: данная страница устарела. Информация может быть неточной или не актуальной.'''<br />
<br />
<br />
Привет всем :) В этой статье мы попытаемся разобраться, как можно изменять параметры существующих моделей в диптауне, для достижения нужного нам эффекта. Делать мы это будем, как вы уже наверное догадались, на примере демосцены с машиной. Те кто уже имел удовольствие поиграть с текущей версией демки, могли заментить, что машина ведет себя как то странно, если не сказать плохо. Она плохо управляется и сильно скользит по поверхности (язык не поворачивается сказать "по дороге"). Мы попробуем разобраться в чем дело, и настроить параметры таким образом, чтобы наша модель была больше похожа на настоящую машину, а не игрушечную машинку, ведущую себя как корова на льду.<br />
<br />
Для работы нам понадобится устновленная версия [[Deeptown SDK]], а так же любой текстовый редактор, позволяющий изменять текстовые файлы в UNIX формате. В windows такими редакторами являются wordpad и, конечно, встроенный редактор в FAR Manager-е.<br />
<br />
== Постановка задачи ==<br />
<br />
Приведем здесь список проблем, которые были выяснены в ходе "эксплуатации" модели:<br />
* Машина медленно разгоняется и очень плохо тормозит<br />
* На больших скоростях заметно хуже управляется<br />
* Опять же, очень сильно заносит и при больших скоростях<br />
* При резком развороте задним ходом, колеса выворачиваются в противоположную сторону<br />
* Резкий разворот при движении вперед практически невозможно сделать; машина просто поворачивает по большой дуге<br />
<br />
Если присмотреться к передним колесам машины в момент старта с места, то можно заметить, что почти сразу начинают проскальзывать, и сцепления с поверхностью практически нет. То же самое происходит и при попытке торможения движением в обратную сторону — колеса проворачиваются и не оказывают заметного эффекта. <br />
<br />
== Настройка трения ==<br />
<br />
Для начала, рекомендую вам обратиться к соответствующей [[Понятие объекта в пространстве|документации]], для того чтобы лучше понимать что мы будем делать.<br />
<br />
Собственно, решение проблемы кроется в настройке коэффициента трения между колесами и поверхностью. Для этого, заходим в файл описания модели машины, расположенный по адресу <tt>Media/storage/media/models/test.model.xml</tt>. Нас интересуют секции описания колес:<br />
<source lang="xml"><br />
<body name="wheel-fl"> <!-- описание левого переднего колеса --><br />
<friction value="1000" /> <!-- коэффициент трения (оно!) --><br />
<mass_data><br />
<mass value="25" /> <!-- масса колеса в кг --><br />
</mass_data><br />
<geometry><br />
<!-- в физическом пространстве, колесо предсталвяется цилиндром --><br />
<geom type="cylinder" radius="0.337575" length="0.228778"><br />
<!-- вращение задается кватернионом --><br />
<rotation w="0.707106781186548" x="0" y="0.707106781186547" z="0" /><br />
</geom><br />
</geometry><br />
<surfaces><br />
<!-- графическое представление колеса (меш) --><br />
<mesh name="wheel-fl" node="/media/meshes/t01.mesh" /><br />
</surfaces><br />
<!-- положение --><br />
<position x="0.724081" y="-0.467315" z="1.28295" /><br />
</body><br />
</source><br />
<br />
Находим параметр ''friction'' и увеличиваем его где-то до 1600. Не забывайте, что менять этот параметр надо для всех четырех колес! Это значение было определено мной экспериментальным путем. Меньшие значения приводят к тому что машина сильно скользит как по льду, бо'льшие значения улучшают управляемость до определенного уровня, а затем колеса просто перестают вращаться :) Связано это с тем, что двигатель уже не может провернуть колеса с большим трением. Поскольку поведение по умолчанию нас не очень устраивает, придется так же настроить мощность двигателя. Тюнить так тюнить! :)<br />
<br />
== Настройка двигателя ==<br />
<br />
Двигатель описан в том же файле, чуть ниже. Ищем секцию ''motor'' и смотрим:<br />
<source lang="xml"><br />
<motor name="engine" url="joint_speed:j-fl/1,j-fr/1"><br />
<limit param="speed" lo="-180" hi="180" /><br />
<limit param="force" lo="0" hi="600" /><br />
</motor><br />
</source><br />
<br />
Любой двигатель описывается несколькими параметрами, которые непосредственно влияют на его поведение. Первый параметр, это ''url'' связанных с двигателем суставов, на которые он воздействует (в данном случае — крутит; бывают и другие типы двигателей). В нашем случае, двигатель с именем ''engine'' воздействует на 2 сустава: "j-fl/1" "j-fr/1". Суставы описаны выше по тексту. Единички в идентификаторе сустава обозначают оси, которые нам нужны. У суставов передних колес по две оси: одна является собственно осью колеса (оно вращается по этой оси), а другая ось проходит перпендикулярно первой — это уже рулевое управление. В случае основного мотора, нам конечно нужна основная ось колеса. <br />
<br />
Еще у моторов есть ограничительные параметры: диапазоны изменения скоростей и прикладываемой силы — ''speed'' и ''force'' соответственно. Примером высокоскоростного, но слабого мотора может послужить двигатель бор машинки (брр); примером медленного, но сильного — двигатель в лифте или эскалаторе. А может быть и то и другое, зависит от места применения конкретного мотора. Эти параметры вводятся для того, чтобы исключить возможность "силового мошенничества" в основном пространстве Диптауна (чтобы не плодить "суперменов").<br />
<br />
В общем, нам нужно увеличить верхнюю границу силы нашего мотора, до величины порядка 1200. Важно понимать, что ставить заоблачные значения силы бесполезно — колеса просто будут вращаться на месте, не оказывая воздействия.<br />
<br />
== Настройка рулевого управления ==<br />
<br />
Теперь попробуем решить проблему с рулевым управлением. Напомню, она проявляется на больших скоростях, либо при попытке сделать "полицейский разворот", это когда машина разворачивается на 180 градусов практически на месте. Для этого нужно начать двигаться задним ходом, а затем резко вывернуть руль. Проблема в том, что при попытке совершить такой разворот, колеса выворачиваются в обратную сторону и иногда даже застревают в таком положении. Иногда бывает даже, что колеса машины не получается повернуть до тех пор, пока мы не начнем движение (те кто ездил на советских машинах, меня поймут ;)<br />
<br />
Все упирается опять же, в силу мотора, но в этот раз уже поворачивающего колеса. Проблемы управляемости на больших скоростях являются следствием возникающего гироскопического эффекта в колесах (да да, физика тут на уровне :). Задумайтесь: колесо весит 25 кг. При вращении с большими скоростями, оно начинает вести себя как настоящий гироскоп (инертность то большая!), в результате оно препятствует изменениям оси своего вращения (что происходит при повоторах). Чем резче поворот, тем больше колесо сопротивляется. <br />
<br />
Решить эту проблему можно либо уменьшив вес колес, либо увеличив силу мотора поворачивающего колеса. Я пошел по второму пути. Принцип тот же, что и в предыдущем случае. Ищем описание мотора с именем turn (поворот) и увеличиваем его силу.<br />
<source lang="xml"><br />
<motor name="turn" url="joint_speed:j-fl/0,j-fr/0"><br />
<limit param="speed" lo="-1" hi="1" /><br />
<limit param="force" lo="0" hi="300" /><br />
<limit param="position" lo="-0.6" hi="0.6" /><br />
</motor><br />
</source><br />
<br />
Не долго думая, меняем значение с 300 на 3000. Ничего страшного, что наш "гидроусилитель" получился сильнее основного мотора машины :) Рычаг то тут больше.<br />
<br />
<br />
В принципе, уже можно попробовать применить наши изменения и посмотреть что получилось. Для этого нам нужно сконвертировать XML описание модели в понятный движку диптауна бинарный формат BXL. Это делает утилита bxd, входящая в стандартный набор утилит Deeptown SDK. Для ее использования надо открыть консоль вашей ОС (в windows это Пуск->Выполнить->cmd.exe, хотя лучше использовать FAR), затем перейти в директорию models и ввести команду:<br />
bxd test.model.xml -f test.model<br />
<br />
Если все пройдет успешно, то программа закончит работу и завершится без дополнительных сообщений (это традиционное поведение UNIX). Выводиться будут только сообщения об ошибках.<br />
<br />
'''Примечание''': Утилита умеет выполнять и обратную операцию. Более подробно это описано в краткой справке к программе, которую можно вызвать, введя команду:<br />
bxd --help<br />
<br />
Теперь можно попробовать запустить дип и покататься на обновленной машине :) Разница должна быть заметна сразу. Если что-то не так, перепроверьте ваши изменения, возможно где-то ошибка. Проверьте так же, действительно ли файл модели сконвертировался успешно.<br />
<br />
== Тормоза и ручник ==<br />
<br />
Наконец, попробуем пойти еще дальше и приделать к нашему болиду нормальные тормоза. Для начала, ограничимся режимом экстренного торможения, когда все колеса машины блокируются полностью. Эта операция будет несколько сложнее чем предыдущие, потому что мы будем добавлять новые секции в файл описания модели и править ее скрипт.<br />
<br />
В настоящий момент, все скрипты пишутся на языке [[K++]]. В принципе, этот язык довольно понятный, особенно людям, знакомым с Си подобными языками, вроде JavaScript или PHP. При возникновении вопросов, обращайтесь к [[K++|документации]], либо [http://forum.deeptown.org на форум].<br />
<br />
Для начала, рассмотрим существующий скрипт и прокомментируем основные части. Для экономии места и вашего внимания, здесь я привожу только интересующие нас места:<br />
<source lang="kpp" line=1><br />
class Bonnet extends WorldObject {<br />
var Motor m_engine;<br />
var Motor m_turn;<br />
var int m_engine_start_count;<br />
var int m_turn_start_count;<br />
public:<br />
function void State_init() {<br />
//...<br />
m_engine = this.getMotor('engine');<br />
m_turn = this.getMotor('turn');<br />
//... <br />
}<br />
<br />
function void startEngine(const real speed, const real force) {<br />
if(!m_engine.isEnabled()) {<br />
m_engine.setParam('speed', speed);<br />
m_engine.setParam('force', force);<br />
m_engine.enable();<br />
}<br />
++m_engine_start_count;<br />
}<br />
<br />
function void stopEngine() { if(--m_engine_start_count == 0) m_engine.disable(); }<br />
<br />
function void startTurn(const real speed, const real force) {<br />
if(!m_turn.isEnabled()) {<br />
m_turn.setParam('speed', speed);<br />
m_turn.setParam('force', force);<br />
m_turn.enable();<br />
}<br />
++m_turn_start_count;<br />
}<br />
<br />
function void stopTurn() { if(--m_turn_start_count == 0) m_turn.disable(); }<br />
<br />
function void Control_stop_engine(const bytea arg) { stopEngine(); }<br />
function void Control_forward (const bytea arg) { startEngine(180, 600); }<br />
function void Control_backward (const bytea arg) { startEngine(-180, 600); }<br />
function void Control_break (const bytea arg) { startEngine(0, 600); }<br />
function void Control_stop_turn (const bytea arg) { stopTurn(); }<br />
function void Control_left (const bytea arg) { startTurn(-1, 300); }<br />
function void Control_right (const bytea arg) { startTurn(1, 300); }<br />
</source><br />
<br />
<br />
;2-5: В теле класса Bonnet объявляются четыре поля (переменных): два мотора и два счетчика. Счетчики можно понимать просто как числа, в то время как моторы являются объектами. Ниже, в них мы положим реальные объекты, соответствующие двум моторам нашей модели.<br />
;7-12: Метод <tt>State_init()</tt> вызывается однократно, при загрузке модели и служит для выполнения различных операций инициализации. В нашем случае мы присваиваем переменным ''m_engine'' и ''m_turn'' объекты моторов, которые получаем по имени. Это те самые имена, которые были описаны в файле модели.<br />
;14-23: Объявляются два метода <tt>startEngine()</tt> и <tt>stopEngine()</tt> которые устанавливают желаемый режим работы, а так же включают и выключают двигатель. Когда двигатель "включен", то он воздействует на ассоциированные с ним объекты; когда выключен — нет. Ну это я думаю и так понятно :)<br />
;25-43: В этих строках делается по сути то же самое, но уже для мотора рулевого управления.<br />
;36-42: А здесь объявляется кучка методов, которые обрабатывают ввод пользователя (нажатия клавиш) и вызывают соответствующие методы скрипта. Чтобы понять как это работает, надо открыть файл <tt>Media/storage/etc/world/input.conf</tt>:<br />
# Input context for local physics tests<br />
context local_control<br />
<br />
# Send event on key press<br />
on key_press check(key=LEFT) do send_event:left<br />
on key_press check(key=RIGHT) do send_event:right<br />
on key_press check(key=UP) do send_event:forward<br />
on key_press check(key=DOWN) do send_event:backward<br />
on key_press check(key=SPACE) do send_event:break<br />
on key_release check(key=LEFT) do send_event:stop_turn<br />
on key_release check(key=RIGHT) do send_event:stop_turn<br />
on key_release check(key=UP) do send_event:stop_engine<br />
on key_release check(key=DOWN) do send_event:stop_engine<br />
on key_release check(key=SPACE) do send_event:stop_engine<br />
<br />
:Здесь объявлены привязки конкретных клавиш к событиям ввода. Работает это так: когда движок ввода обнаруживает, что пользователь нажал клавишу, например кнопку ВВЕРХ, то он смотрит у себя в таблицах и находит действие "send_event:forward", что в конечном счете приведет к тому, что в скрипте управляемой модели будет вызван метод <tt>Control_forward()</tt>. Префикс "Control_" приписывается автоматически. <br />
<br />
Однако, ближе к делу. Для того чтобы сделать полноценные тормоза, там надо добавить в модель еще один мотор, который будет противодействовать движению машины, а так же реализовать соответствующий интерфейс в скрипте, управляющий этми мотором. <br />
<br />
Опять же, открываем файл <tt>Media/storage/media/models/test.model.xml</tt> и добавляем в него следующую секцию, рядом с существующими моторами:<br />
<source lang="xml"><br />
<motor name="breaks" url="joint_speed:j-fl/1,j-fr/1,j-bl/0,j-br/0"><br />
<limit param="speed" lo="-1" hi="1" /><br />
<limit param="force" lo="0" hi="100000" /><br />
</motor><br />
</source><br />
<br />
Обратите внимание, что этот мотор действует на все четыре колеса; соответственно, в его параметре ''url'' прописаны все 4 сустава колес. В случае суставов задних колес, наличествует только одна ось, поэтому указан иднекс 0. При экстренном торможении, на колеса приходится очень большая нагрузка, поэтому сила противодействия тоже должна быть приличной. Соответственно указывается большое значение силы.<br />
<br />
Принцип работы такой: когда требуется затормозить, мы включаем мотор "breaks", с помощью метода <tt>enable()</tt>, установив желаемую скорость в 0. Таким образом, мотор будет очень сильно стараться остановить вращение оси, независимо от направления ее вращения. <br />
<br />
Осталось только реализовать управление этим мотором из скрипта, поскольку все привязки в конфигурации уже есть. Параллельно с этим, мы уберем счетчики, поскольку как оказалось, они не влияют на работу модели. <br />
<br />
Для этого мы должны объявить еще одну переменную m_breaks, рядом с существующими:<br />
<source lang=kpp><br />
class Bonnet extends WorldObject<br />
{<br />
var Motor m_engine;<br />
var Motor m_turn;<br />
var Motor m_breaks; //добавляем переменную<br />
var int m_engine_start_count; //а это убираем<br />
var int m_turn_start_count; //и это тоже<br />
</source><br />
<br />
Далее, в метод <tt>State_init()</tt> прописываем строчку для инициализации нашей переменной, а так же задаем параметры:<br />
<source lang=kpp><br />
function void State_init() {<br />
//...<br />
m_engine = this.getMotor('engine');<br />
m_turn = this.getMotor('turn');<br />
m_breaks = this.getMotor('breaks'); //получаем мотор по имени<br />
<br />
m_breaks.setParam('speed', 0); //желаемая скорость всегда 0<br />
m_breaks.setParam('force', 100000); //силу по максимуму<br />
//...<br />
}<br />
</source><br />
<br />
Меняем управляющие методы:<br />
<source lang=kpp><br />
function void Control_stop_engine(const bytea arg) { stopEngine(); }<br />
function void Control_forward (const bytea arg) { m_breaks.disable(); startEngine(180, 1200); }<br />
function void Control_backward (const bytea arg) { m_breaks.disable(); startEngine(-180, 1200); }<br />
function void Control_break (const bytea arg) { m_breaks.enable(); }<br />
function void Control_stop_turn (const bytea arg) { stopTurn(); }<br />
function void Control_left (const bytea arg) { startTurn(-1, 3000); }<br />
function void Control_right (const bytea arg) { startTurn(1, 3000); }<br />
</source><br />
<br />
Обратите внимание, что мы поменяли величины силы для всех моторов в соответствии с нашими изменениями. Ну и подправили код методов таким образом, что при нажатии на тормоз, будет включен "двигатель" тормозов, а при нажатии на кнопки движения он будет автоматически выключен (чтобы нельзя было поехать на ручнике :).<br />
<br />
Дополнительно, надо поправить оставшиеся методы, убрав из них упоминания о счетчиках:<br />
<source lang=kpp><br />
function void startEngine(const real speed, const real force) {<br />
if(!m_engine.isEnabled()) {<br />
m_engine.setParam('speed', speed);<br />
m_engine.setParam('force', force);<br />
m_engine.enable();<br />
}<br />
//++m_engine_start_count;<br />
}<br />
<br />
function void stopEngine() { /*if(--m_engine_start_count == 0) */m_engine.disable(); }<br />
<br />
function void startTurn(const real speed, const real force) {<br />
if(!m_turn.isEnabled()) {<br />
m_turn.setParam('speed', speed);<br />
m_turn.setParam('force', force);<br />
m_turn.enable();<br />
}<br />
//++m_turn_start_count;<br />
}<br />
<br />
function void stopTurn() { /*if(--m_turn_start_count == 0) */m_turn.disable(); }<br />
</source><br />
<br />
С кодом мы вроде как разобрались, осталось только его скомпилировать. Для этого, нужно опять же взять консоль, но уже перейти в директорию scripts. И выполнить следующую команду:<br />
kpp bonnet.kpp<br />
<br />
Если мы все сделали правильно, то программа скомпилируется без ошибок. Соответственно, компилятор выйдет, не сказав ни слова. В противном случае, он напишет отчет об ошибках, где будет указаны сообщения об ошибке и номер строки, на которой они возникают. Посмотрите на эти строки и сравните с тем что написано здесь. Возможно, вы пропустили какой то символ, либо написали слово с ошибкой.<br />
<br />
== Ура, работает! ==<br />
<br />
Если это действительно так, то мои вам поздравления :) Теперь можно выписывать круги на нашем виртуальном автодроме, дрифтить и разворачиваться на ручнике :) Если же что-то не получилось, — не отчаивайтесь. Пишите свои вопросы [http://forum.deeptown.org на форум] и делитесь своими мыслями с другими участниками.<br />
<br />
== Домашнее задание ==<br />
<br />
Если вы горите желанием что-нибудь еще эдакое сделать, то вот вам список идей которые вы можете поробовать реализовать самостоятельно :) Не забывайте только перекомпилировать скрипт (утилита kpp) и конвертировать файл модели после изменения (утилита bxd).<br />
<br />
# Поиграйте со значениями; попробуйте подобрать лучшие соотношения мощностей двигателя и коэффициентов трения. Удачный опыт, можно опять же описать на [http://forum.deeptown.org форуме].<br />
# Попробуйте поставить разные коэффициенты трения для передних и задних колес (а так же установить трение в самих осях).<br />
# Можно переделать машину из переднеприводной в заднеприводную. Для этого надо поправить определение двигателя ''engine'' в файле описания модели. Как именно — решать вам ;) Не забывайте, что поведение машины сильно изменится при этом. Все таки задний привод.<br />
# А можно сделать и полный привод! :)<br />
# А еще можно попробовать сделать и заднюю пару колес управляемой (а то и независимо!). Тогда получится машина с огромной маневренностью.<br />
# Попробуйте сделать нормальные тормоза, а не только ручник. То есть, чтобы машина тормозила плавно, так чтобы колеса не блокировались. Подсказка: это можно сделать с помощью имеющегося двигателя ''m_engine'', просто надо менять режимы его работы. При торможении ставить желаемую скорость в 0 и подбирать параметры.<br />
# Еще можно сделать настоящую подвеску (амортизаторы). Тогда машина сможет лучше ездить по пересеченной местности. Только для этого надо использовать уже линейный мотор. <br />
# Наконец, можно упростить все что мы сделали тут и обойтись без введения дополнительного двигателя, а все сделать через основной.<br />
# Если совсем нечего делать, то можно добавить еще одну пару колес где нибудь =)<br />
# Интересная и нетривиальная задача — попробовать сделать коробку передач. Смысл в том, чтобы максимальная скорость и сила вращения менялись в зависимости от текущей передачи (управление сделать либо ручное, либо "автомат"). Это может помочь решить проблему с проскальзыванием и сделать машину более правдоподобной. Однако тут надо думать...<br />
<br />
В целом, пробуйте и экспериментируйте :) Все в ваших руках. Диапазон возможностей ограничивается только вашей фантазией :) <br />
<br />
В следующем туториале я постараюсь рассказать, как можно добавлять в систему собственные объекты. Это немного сложнее, но только чуть чуть. До встречи! :)<br />
<br />
<br />
С уважением, <br> <br />
[[Участник:Korvin|Korvin]]</div>Raw mathttp://man.deeptown.org/index.php/%D0%9F%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0Программирование поведения объекта2013-07-13T12:12:48Z<p>Raw mat: </p>
<hr />
<div>Перейдем от теории к практике. Перед прочтением этого документа рекомендуется ознакомиться с предыдущими. Здесь приведены конкретные рецепты - как и что делать, с примерами и их разбором.<br />
<br />
== Где хранить файлы? ==<br />
<br />
Ядро Диптауна использует DISS для хранения файлов. По-умолчанию, в корень DISS монтируется директория, которая находится в подкаталоге storage архива media. Таким образом,<br />
* в linux это директория, которая расположена в $DEEPTOWN_MEDIA/storage<br />
* в windows - INSTALL_DIR/Media/storage<br />
<br />
DISS при первом запуске индексирует рекурсивно все файлы своего каталога (это необходимо для выполнения сложных запросов к файловой системе). Поэтому, если Вы добавляете или удаляете какие-либо файлы где-либо в пределах этой директории, нужно удалить файл '''_index''' в ее корне, чтобы при следующем запуске DISS переиндексировал каталог заново, и "увидел" бы внесенные изменения.<br />
<br />
Далее в документе, везде, где указан путь к файлу, имеется ввиду DISS-путь.<br />
<br />
Для хранения объектов, в корне DISS есть подкаталог media, в котором есть следующие подкаталоги:<br />
* materials - для хранения файлов материалов;<br />
* meshes - для графических 3d моделей;<br />
* models - для файлов объектов;<br />
* scenes - для файлов сцен;<br />
* scripts - для файлов скриптов;<br />
* textures - для текстур.<br />
<br />
Отметим, что это лишь рекомендованные пути; Вы можете использовать любые другие в пределах DISS.<br />
<br />
== Создание простого объекта ==<br />
<br />
Для того, чтобы создать объект, нужны<br />
* файл материала;<br />
* файл 3d модели в формате OGRE mesh;<br />
* необходимые материалу текстуры.<br />
<br />
Все это можно получить, воспользовавшись одним из конверторов, доступных на [http://www.ogre3d.org/index.php?option=com_content&task=view&id=413&Itemid=133 официальном сайте OGRE].<br />
<br />
Пожалуйста, обратите внимание, что все размеры в файле 3d модели должны указываться '''в метрах'''.<br />
<br />
Если конвертер создает несколько файлов материалов, нужно объединить их в один (в произвольном порядке). Кроме того, нужно '''переправить пути к текстурам''' в файлах материалов на абсолютные DISS-пути (т.е. '''/media/textures/...''').<br />
<br />
Когда все это сделано, остается последний шаг - создать файл модели. Для этого нужно создать XML-файл объекта, который, в простейшем случае, выглядит следующим образом:<br />
<br />
<source lang="xml"><br />
<model bxd="http://dao.deeptown.org/bxd/model.bxd"><br />
<material_script node="путь к файлу материала" /><br />
<body name="main_body"><br />
<geometry><br />
<!-- описание геометрии объекта --><br />
</geometry><br />
<surfaces><br />
<mesh name="cube" node="путь к файлу 3d модели" /><br />
</surfaces><br />
</body><br />
</model><br />
</source><br />
<br />
Чтобы теперь сконвертировать этот файл в bxl, воспользуйтесь командой<br />
bxd filename.model.xml -f filename.model<br />
<br />
где filename.model.xml - имя исходного файла, filename.model - имя получаемого файла модели.<br />
<br />
Для того, чтобы использовать объект локально, необходимо задать его UID, записав его в метаинформацию DISS. В той же директории, где находится этот файл, создайте файл '''.meta''' и напишите туда следующее:<br />
[filename.model]<br />
version = 0.1<br />
wso_node = 1<br />
wso_uid = 0101-0001-00000001-0000000000000001<br />
<br />
UID (последняя строчка) должен различаться для каждой копии объекта в пространстве. Если один и тот же объект нужно использовать в сцене несколько раз, можно задать ему несколько UID-ов в метаинформации - задав несколько записей wso_uid.<br />
<br />
В самом UID-е можно менять только последний (самый длинный) блок цифр (это шестнадцатиричные цифры, т.е. от 0 до F).<br />
<br />
Если в директории находится несколько объектов, дописывайте в .meta секции одну за другой. Файл .meta сканируется при создании индекса DISS, так что при внесении изменений нужно удалять '''.index'''.<br />
<br />
=== Создание динамического объекта ===<br />
<br />
Объект, приведенный в примере выше, будет статичным, т.е. все время находиться в одном и том же месте пространства (обладать бесконечной массой). Если нужен динамический объект, следует добавить в описание модели информацию о массе и моменте инерции объекта:<br />
<br />
<source lang="xml"><br />
<body name="main_body"><br />
<mass_data><br />
<mass value="1" /><br />
<inertia i11="1" i22="1" i33="1" i12="0" i13="0" i23="0" /><br />
</mass_data><br />
<!-- ... --><br />
</body><br />
</source><br />
<br />
Масса указывается в килограммах.<br />
<br />
Момент инерции - в единицах СИ. Параметры - элементы симметричной матрицы инерции:<br />
| i11 i12 i13 |<br />
| i12 i22 i23 |<br />
| i13 i23 i33 |<br />
<br />
Вы можете не указывать тег '''inertia''' для простых тестов, но в этом случае объект будет вести себя неестественно.<br />
<br />
== Связывание объекта со скриптом ==<br />
<br />
Для того, чтобы получить возможность программировать поведение объекта, нужно прописать в него адрес скрипта. Добавьте внутрь корневого XML-тега следующее:<br />
<br />
<source lang="xml"><br />
<externals><br />
<event_receiver url="gide:/media/scripts/my_object.gbc!MyObject" /><br />
</externals><br />
</source><br />
<br />
В этом случае объект будет передавать все свои события объекту гайд-класса MyObject, объявленного в файле /media/scripts/my_object.gbc.<br />
<br />
В этом файле должен находиться '''скомпилированный''' код класса. Простейший, ничего не делающий код такого класса на [[K++]] выглядит следующим образом:<br />
<br />
<source lang="kpp"><br />
package my_object;<br />
<br />
import world;<br />
<br />
class MyObject extends WorldObject<br />
{<br />
export function State_init()<br />
{<br />
}<br />
}<br />
</source><br />
<br />
Напишите этот код в файл my_object.kpp в той же директории. Компилируется он командой<br />
kpp my_object.kpp<br />
<br />
== Добавление мотора ==<br />
<br />
Мотор - это интерфейс объекта, позволяющий контролировать его поведение.<br />
<br />
Все моторы объекта должны быть описаны в его файле модели. Делается это добавлением тега '''motor''' в корневую секцию:<br />
<source lang="xml"><br />
<motor name="engine" url="linear_speed:main_body:(0,1,0)" /><br />
</source><br />
<br />
Такой тег создаст мотор '''engine''' с типом '''linear_speed''', прикладывающий силу к телу '''main_body''' (параметр '''name''' в теге '''body''') по вектору направления (0,1,0) - т.е. вверх.<br />
<br />
Всевозможные типы моторов описаны (''еще нигде не описаны, дать ссылку'').<br />
<br />
По-умолчанию, мотор будет выключен. Для того, чтобы его включить, добавим в наш код следующее:<br />
<br />
<source lang="kpp"><br />
export function State_init()<br />
{<br />
var engine = this.getMotor('engine'); // получили интерфейс мотора<br />
// первый параметр - имя, указанное при создании мотора (см. выше)<br />
engine.setParam('speed', 100); // максимальная скорость, сообщаемая объекту<br />
engine.setParam('force', 100); // максимальная сила, которая будет приложена для разгона<br />
engine.enable(); // включаем мотор<br />
}<br />
</source><br />
<br />
== Обработка пользовательского ввода ==<br />
<br />
Теперь научим наш объект реагировать на команду пользователя.<br />
<br />
Например, нам нужно, чтобы мотор был включен только пока нажата клавиша A на клавиатуре.<br />
<br />
Для этого нужно научить наш объект понимать две команды - "включить" и "выключить", а в конфигурации движка ввода написать отправку команды "включить" на нажатие клавиши, а "выключить" - на ее отпускание.<br />
<br />
В файл '''/etc/world/input.conf''' в контекст '''local_control''' нужно добавить следующие строки:<br />
on key_press check(key=A) do send_event:start_engine<br />
on key_release check(key=A) do send_event:stop_engine<br />
<br />
Таким образом, когда пользователь нажимает клавишу A, нашему объекту будет отправлено уведомление '''start_engine''', а когда отпускает - '''stop_engine'''.<br />
<br />
Для того, чтобы обработать эти уведомления, напишем следующие два метода в наш класс:<br />
<br />
<source lang="kpp"><br />
export function Control_start_engine(const bytea args)<br />
{<br />
this.getMotor('engine').enable();<br />
}<br />
<br />
export function Control_stop_engine(const bytea args)<br />
{<br />
this.getMotor('engine').disable();<br />
}<br />
</source><br />
<br />
(предполагается, что параметры мотора были установлены при инициализации, т.е. в State_init).<br />
<br />
== Создание сенсора ==<br />
<br />
Предположим, нам нужно, чтобы объект как-то реагировал на происходящее в окружающей среде. Для этого существуют '''сенсоры''' - интерфейсы, передающие информацию о происходящем.<br />
<br />
На данный момент реализован только сенсор высоты, покажем работу с сенсорами на его примере:<br />
<br />
<source lang="kpp"><br />
export function State_init()<br />
{<br />
// создаем сенсор высоты<br />
this.createSensor('height:>100', 'too_high');<br />
}<br />
<br />
export function Signal_too_high(const bytea args)<br />
{<br />
// метод будет вызван при срабатывании сенсора, т.е. когда абсолютная<br />
// высота объекта превысит 100 метров.<br />
this.getMotor('engine').disable();<br />
}<br />
</source><br />
<br />
Имя функции-обработчика формируется из префикса '''Signal_''' и второго параметра функции '''createSensor'''.<br />
<br />
== Создание сцены ==<br />
<br />
Для того, чтобы теперь посмотреть на созданные объекты, нужно создать сцену и разместить на ней эти объекты, и затем загрузить сцену в движок.<br />
<br />
Сцена также описывается в формате BXL. XML-файл сцены выглядит довольно просто:<br />
<br />
<source lang="xml"><br />
<scene bxd="http://dao.deeptown.org/bxd/scene.bxd"><br />
<model uid="0101-0001-00000001-0000000000000002"<br />
x="0.2" y="0" z="-5"<br />
qw="1" qx="0" qy="0" qz="0" /><br />
</scene><br />
</source><br />
<br />
Тегов '''model''' может быть сколь угодно много. В параметрах этого тега указываются координаты объекта на сцене и кватернион его поворота.<br />
<br />
== Пуск! ==<br />
<br />
И теперь остался последний момент - загрузить все это в движок.<br />
<br />
Для этого нужно набрать консольную команду<br />
world.local_start /media/scenes/filename.scene UID<br />
<br />
Первый параметр - имя файла сцены, второй - UID объекта, которому будет передаваться ввод пользователя.</div>Raw mathttp://man.deeptown.org/index.php/%D0%9F%D0%BB%D0%B0%D1%82%D1%84%D0%BE%D1%80%D0%BC%D0%B0_GideПлатформа Gide2013-07-13T12:07:47Z<p>Raw mat: </p>
<hr />
<div>''страница требует правки''<br />
<br />
* [[Описание платформы Gide]]</div>Raw mathttp://man.deeptown.org/index.php/%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5Переменные2013-07-13T12:02:49Z<p>Raw mat: </p>
<hr />
<div>__TOC__<br />
== Понятие переменной, тип переменной ==<br />
<br />
В классических книгах по программированию, под переменными понимают некоторую поименованную область памяти, либо регистр процессора. В нашем же случае, мы имеем дело с виртуальной машиной, соответственно ни регистров, ни памяти в обычном понимании у нас нет. Все, с чем мы имеем дело, это с объектами в "сознании" виртуальной машины. Так или иначе, но и там и тут переменные служат для одной цели: для хранения некоторой информации. Ей могут быть и исходные данные программы, и некоторые промежуточные значения, или же результаты выполнения программы. Впрочем, некоторые программы могут не иметь исходных данных (например, генератор текстур), другие же могут не производить никакого вывода, так что все это довольно условно.<br />
<br />
Поэтому, будем считать проще, что переменная это просто некоторая сущность, некоторый объект, способный хранить данные. Под данными мы будем понимать любую информацию, которую возможно представить в цифровом виде.<br />
<br />
=== Тип переменной ===<br />
<br />
Часто с понятием переменной связывают понятие ее типа. Стоит отметить, что это понятие так же является математической абстракцией. С точки зрения самой машины все данные представляют собой одно и то же — совокупность нулей и единиц. А что под ними понимать в каждом конкретном случае — решает уже программист. Так вот, в некотором смысле ''тип переменной'' показывает, что именно хранится в данной переменной. <br />
<br />
Наиболее распространенные типы переменных это:<br />
* Логический тип, принимающий только два значения ("истина" и "ложь")<br />
* Целые числа различной длины (байт, слово, двойное слово и т. д.)<br />
* Числа с плавающей точкой<br />
* Строки (последовательность байтов в памяти, воспринимаемая как строка текста)<br />
* Указатели (переменные, содержащие в себе адрес другой переменной)<br />
* Составные типы (некоторая совокупность из вышеперечисленных типов)<br />
<br />
Как уже говорилось выше, первоначально о типе переменной знал только программист, пишущий программу. Естественно, вся ответственность за соблюдение соответствия типов выполняемым операциям была возложена на самого программиста. Постепенно, с развитием языков программирования высокого уровня, была выдвинута идея явным образом сопоставлять переменной ее тип. Это обеспечивало большее удобство в написании программ, поскольку всю работу по контролю над типами выполнял сам компилятор, который выдавал сообщения об ошибках, если типы переменных не соответствовали операциям, или была вероятность неверного их истолкования. Так появились языки со статической типизацией.<br />
<br />
== Статическая типизация на примере C++ ==<br />
<br />
Итак, ''статическая типизация'' это — приём, при котором переменная, параметр подпрограммы или возвращаемое значение функции связывается с типом в момент объявления и этот тип не может быть изменён позже (переменная или параметр будут принимать, а функция будет возвращать значения только этого типа). <br />
<br />
То есть, если мы объявляем переменные целочисленного типа как <tt>int i, j;</tt> то мы можем выполнять над ними только операции, соответствующие понятиям этого типа, вроде операции сложения <tt>i + j</tt> или операции присваивания одной переменной другой <tt>i = j</tt>. Если же мы попытаемся присвоить такой переменной значение другого, ''неприводимого'' типа, например <tt>i = [2, 3]</tt> то компилятор выдаст ошибку.<br />
<br />
Рассмотрим кратко преимущества и недостатки статически типированных языков и сравним их впоследствии с другими подходами к типизации.<br />
<br />
Преимущества статической типизации:<br />
* Строгость программ<br />
* Большая возможность оптимизации, а, следовательно:<br />
** Высокая скорость работы<br />
** Минимальные требования к памяти<br />
<br />
Недостатки:<br />
* Необходимость объявления типов даже там где это очевидно<br />
* Засорение кода программы дополнительными символами, а, следовательно:<br />
** Отвлечение внимания программиста от самой задачи в сторону рутины формализации<br />
** Плохая читаемость кода<br />
* Необходимость дополнительных ухищрений для передачи параметров разных типов<br />
* Невозможность написания абстрактных алгоритмов<br />
<br />
'''Вывод:''' Программы, написанные на языках со статической типизацией, обладают большой эффективностью и производительностью, однако это дается ценой строгости и определенности. Программирование на таких языках это принятие их правил игры, при этом программист должен мыслить в жестко ограниченных рамках. Вот почему эти языки чаще всего применяются именно для системного программирования, где в первую очередь важна производительность полученного кода, и только потом удобство его написания и поддержки.<br />
<br />
== Динамическая типизация на примере Ruby ==<br />
<br />
Впоследствии, в противовес статической типизации была разработана модель динамически типированных языков. При таком подходе, переменная не имеет заранее определенного типа, а принимает тип при инициализации и при присвоении ей некоторого значения. Таким образом, в различных участках программы одна и та же переменная может принимать значения разных типов. <br />
<br />
На первый взгляд это может показать странным, особенно с точки зрения программиста, привыкшего к статически типированным языкам (таким как С++ или Паскаль). На самом деле здесь нет ничего страшного или нелогичного. Все мы в своей жизни, сами того не подозревая, оперируем понятиями динамических переменных. Один и тот же объект в разных ситуациях мы можем воспринимать с разных точек зрения. Задавая вопрос "как пройти в библиотеку", мы можем получить как конкретный ответ "в 3 часа ночи?! идиот!" так и целый набор: "на автобусе номер 23, на такси или пешком, тут не очень далеко". При этом нас совершенно не смущает то, что мы заранее не знаем, каков будет результат, наоборот — это дает нам большую гибкость и свободу действий; мы двигаемся дальше на основании того, каков был результат предыдущей операции. Подобно этому примеру, мы можем писать программы, которые оперируют переменными неопределенного типа. В результате, код получается более лаконичным, более читаемым и, в конце концов, более близким к естественному восприятию человека (ведь именно этого мы ждем от языков программирования!).<br />
<br />
Концепция динамической типизации в полной мере нашла свое применение в языке Ruby. В этом языке любая переменная может в разное время иметь различные типы. Вот пример кода на этом языке:<br />
<br />
<source lang="ruby"><br />
def get_data(need_array = false)<br />
result = 0 # целое число<br />
if need_array<br />
result = [ 1, 2, 3, 4 ] # тип меняется на Array<br />
else<br />
result = { 1 => 2, 3 => 4} # тип меняется на Hash<br />
end<br />
result<br />
end<br />
</source><br />
<br />
Преимущества динамической типизации:<br />
* Легкость написания программ<br />
* Лаконичность и хорошая читаемость<br />
* Высокая гибкость языка. Возможность решения проблемы разными способами<br />
* Большая возможность повторного использования кода (абстрактные алгоритмы)<br />
<br />
Недостатки:<br />
* Существенно меньшая производительность, по сравнению со статически типированными языками<br />
* Сложность в написании оптимизаторов, их малая эффективность<br />
* Как правило, динамически типированные языки являются интерпретаторами (компиляция не столь эффективна)<br />
<br />
'''Вывод:''' Языки с динамической типизацией очень удобны для использования на прикладном уровне. Учитывая их высокую гибкость и лаконичность, программисту легче излагать свои мысли, при том, что свое внимание он концентрирует на самой задаче, не отвлекаясь на частные проблемы реализации. Конечно, они уступают по эффективности статическим, компилируемым языкам, однако это уже вопрос, требующий отдельного рассмотрения в рамках конкретно поставленной задачи.<br />
<br />
== Полудинамическая типизация ==<br />
<br />
Рассмотрев преимущества и недостатки статической и динамической типизации, мы подходим к мысли объединения этих двух подходов в рамках одного языка. Языка, который бы сочетал в себе все преимущества динамической типизации, с возможностью указания типов там, где это необходимо. В результате мы получили бы язык, достаточно гибкий и в то же время позволяющий эффективно работать с типами.<br />
<br />
Язык К++ как раз и ставит перед собой задачу совмещения этих двух подходов. Переменные этого языка могут быть либо типированными, либо не типированными (динамическими). Первые обеспечивают производительность, вторые — гибкость. Достигается это, прежде всего ценой того, что переменная не может изменять свой тип во время выполнения программы. Однако это относится только к статически типированным переменным. Полностью динамические переменные этого недостатка лишены. <br />
<br />
При разработке программы на языке К++, программист должен стараться держать баланс между удобством написания программы и ее эффективностью. В целом, рекомендуется указывать типы переменных везде, где это возможно. Динамические же переменные следует применять в тех местах, где заранее неизвестно, с каким типом переменной придется работать. <br />
<br />
== Типизация при объявлении ==<br />
<br />
Язык К++ позволяет задавать тип переменной несколькими способами. Одним из них является типирование переменной при объявлении. При этом необходимо написать ключевое слово <tt>'''var'''</tt>, за которым следует [[идентификатор]] типа переменной, а затем ее имя. Точка с запятой (;) завершает конструкцию:<br />
<br />
<source lang="kpp"><br />
var int i;<br />
</source><br />
<br />
Здесь мы объявили [[Стандартные типы данных#Числа|целочисленную]] переменную с именем ''i'' и неявно [[Инициализация|инициализировали]] ее значением 0. Такой синтаксис удобен в тех случаях, когда заранее неизвестно каким должно быть значение переменной. Видимо, в данном случае программист решил, что оно будет присвоено явным образом впоследствии. Если же заранее ясно, что на момент объявления переменная должна иметь определенное значение, следует применять синтаксис ''объявления с инициализацией''.<br />
<br />
'''Примечание:''' Неявная инициализация это причина многих трудноуловимых ошибок времени выполнения. В языке К++ это имеет менее разрушительные последствия, чем например в С++ (где значение неинициализированной переменной может быть абсолютно произвольным!), тем не менее '''настоятельно рекомендуется инициализировать переменные''' сразу при объявлении. Это избавит вас от опасности забыть присвоить значение переменной впоследствии.<br />
<br />
== Типизация при инициализации ==<br />
<br />
Более предпочтительным способом объявления типированной переменной является ''объявление с инициализацией''. В данном случае, под ''инициализацией'' понимается процесс начального задания значения переменной. Для этого следует так же написать ключевое слово <tt>'''var'''</tt>, после которого сразу идет [[идентификатор]] имени переменной, а затем выражение — инициализатор, присваивающее значение вновь созданной переменной. При этом тип переменной будет определен как тип инициализатора:<br />
<br />
<source lang="kpp"><br />
var i = 0;<br />
var s = "hello world";<br />
</source><br />
<br />
В приведенном выше примере, переменной ''i'' будет назначен целочисленный тип (<tt>[[Стандартные типы данных#Целые числа|int]]</tt>), в то время как переменная ''s'' будет иметь строковый тип (<tt>[[Стандартные типы данных#Строки|string]]</tt>).<br />
<br />
'''Примечание:''' В некоторых случаях может сложиться ситуация, что инициализатор переменной это довольно сложное выражение. При этом программисту сложно определить, какой же на самом деле будет тип переменной. Для ясности приведем пример из разряда "как не надо делать":<br />
<br />
<source lang="kpp"><br />
var x = 2+3*4+f(3)/3*0.2;<br />
</source><br />
<br />
Синтаксически здесь все верно. Переменная будет создана и ей будет назначен некоторый тип. Но вот какой? Опытный читатель заметит, что в выражении присутствуют константы с плавающей точкой и операция деления. Стало быть, в результате [[Приведение типов|приведения типов]], результирующий тип переменной так же будет [[Стандартные типы данных#Числа с плавающей точкой|числом с плавающей точкой]]. Внимательный же читатель заметит так же, что в выражении присутствует [[Выражения#Вызов функций|вызов функции]] <tt>f(3)</tt>. И тут дело обстоит еще хуже. [[Функции|Функция]] может возвращать значение любого типа. Чтобы узнать какого именно, нужно обратиться к документации, либо к описанию самой функции. А что если она возвращает нетипированную переменную ([[Переменные#Нетипированные (динамические) переменные|см. ниже]])? Тогда предсказать тип переменной будет еще сложнее (если забежать вперед, то можно сказать что результат выражения так же будет нетипирован).<br />
<br />
Скорее всего, Читатель уже согласился с мыслью, что подобных конструкций следует избегать. Однако что же делать, если все же требуется завести переменную, которая должна быть инициализирована подобным образом? Выхода тут может быть два. Либо отделить объявление переменной от ее инициализации, либо совместить типированное объявление с инициализацией.<br />
<br />
Первый вариант:<br />
<source lang="kpp"><br />
var real x;<br />
... //где то по коду программы<br />
x = 2+3*4+f(3)/3*0.2; <br />
</source><br />
<br />
Второй, более удачный вариант:<br />
<source lang="kpp"><br />
var real x = 2+3*4+f(3)/3*0.2;<br />
</source><br />
<br />
Стоит отметить, что во втором случае будет выполнена операция [[Приведение типов|приведения типов]], то есть будет сделана попытка преобразовать тип значения инициализатора к явно указанному типу переменной. Если тип инициализатора возможно ''привести'' к типу переменной, то все пройдет успешно. Если же типы ''неприводимы'', то произойдет либо ошибка компиляции (при статическом типе инициализатора), либо ошибка времени выполнения (при динамическом).<br />
<br />
== Нетипированные (динамические) переменные ==<br />
<br />
Иногда бывает необходимо работать с переменной, тип которой на момент компиляции неизвестен. В этом случае применяются ''нетипированные'' или ''динамические'' переменные. Такие переменные объявляются просто, путем написания ключевого слова <tt>'''var'''</tt>, за которым идет [[идентификатор]] имени переменной:<br />
<br />
<source lang="kpp"><br />
var x;<br />
</source><br />
<br />
В ходе работы программы, такая переменная может принимать значения любых типов. Она будет автоматически приводиться к нужным типам (и наоборот), что, в конечном счете, обеспечивает гибкость кода.<br />
<br />
Примером применения динамических переменных могут послужить контейнеры — объекты, которые могут хранить в себе другие объекты. Простейшими примерами контейнеров могут послужить [[Стандартные типы данных#Массивы|массивы]]. В языке К++ массивы не являются типированными. Они могут хранить любые объекты, даже объекты различных типов. Разумеется, сам массив оперирует со своими элементами как с нетипированными переменными (ведь он не знает заранее, объекты каких типов будут в него класть). <br />
<br />
Программист может написать некоторую подпрограмму для обработки элементов массива, например для их сортировки. При этом все, что ему нужно знать это способ перебора элементов контейнера и некоторую операцию сравнения двух элементов, для выяснения их очередности (эту операцию контейнеру предоставляет пользователь; как именно это делается, будет описано [[Блоки#Применение блоков|позднее]]). Этим достигается эффективность написания кода, и открываются широкие возможности по повторному использованию кода. К примеру, в традиционных типированных языках вроде Паскаля, пришлось бы реализовывать отдельные методы сортировки строк, числовых массивов и т. д., здесь же мы обходимся одной реализацией.<br />
<br />
== О важности инициализации переменных ==<br />
<br />
В заключение этой главы, приведем краткое обобщение материала относительно инициализации переменных. Как уже было сказано выше, инициализация переменных это важный момент, правильное понимание которого позволит программисту писать более эффективные программы, и в то же время избавить себя от многих типичных ошибок программирования. В ходе изучения языка постарайтесь выработать у себя некоторые шаблоны проектирования, следуя которым вы избавите себя от возможных проблем.<br />
<br />
Вот некоторый список рекомендаций относительно объявления переменных:<br />
* Старайтесь всегда объявлять переменные, инициализируя их<br />
* Если выражение инициализатора достаточно сложное, либо содержит вызовы функций, применяйте явное типирование объявляемой переменной, либо используйте [[приведение типов]] в самом выражении<br />
* Объявляйте переменные как можно ближе к месту их использования<br />
* Нетипированные переменные применяйте только в тех случаях, где это действительно необходимо<br />
<br />
Конечно, этот список далеко не полный, да он и не претендует на полноту. То, что написано выше ни в коем случае не является правилом, которому нужно следовать непременно, везде и всегда. Границы разумного определяете вы сами. Просто старайтесь не упускать этих моментов из виду.</div>Raw mathttp://man.deeptown.org/index.php/%D0%9F%D0%BE%D0%BD%D1%8F%D1%82%D0%B8%D0%B5_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%B0_%D0%B2_%D0%BF%D1%80%D0%BE%D1%81%D1%82%D1%80%D0%B0%D0%BD%D1%81%D1%82%D0%B2%D0%B5Понятие объекта в пространстве2013-07-13T11:59:07Z<p>Raw mat: </p>
<hr />
<div>== Введение ==<br />
<br />
Ключевым понятием в World Engine является Объект. Под этим понятием подразумевается некоторая атомарная сущность, которая может взаимодействовать с:<br />
* пространством<br />
* пользователем<br />
* другими объектами.<br />
<br />
Понятие объекта, в общем случае, шире, чем некоторое физическое тело в виртуальном пространстве. Объект может даже не обладать координатами в пространстве: весь его смысл в этом случае заключается во взаимодействии, которое не обязано быть физическим. Тем не менее, большая часть объектов — это именно объекты виртуального пространства.<br />
<br />
== Представления объекта ==<br />
<br />
Каждый объект может быть представлен в нескольких (а может быть не представлен ни в одном) из трех пространств: механическом, физическом и графическом. Все три представления связаны между собой, но важно понимать, что это — три '''разных''' представления.<br />
<br />
В '''механическом''' пространстве, объект представляет собой одно или несколько твердых тел, каждое из которых характеризуется:<br />
* массой,<br />
* матрицей момента инерции,<br />
* координатами,<br />
* [http://www.gamedev.ru/users/wat/articles/quaternions кватернионом] вращения.<br />
<br />
Обратите внимание, что в механическом пространстве '''отсутствует''' какая-либо информация о '''форме''' объекта.<br />
<br />
Если таких тел несколько, они могут быть связаны между собой '''суставами'''. Сустав определяет некоторые условия, по которым перемещение одного тела влечет перемещение другого. Существует несколько различных типов суставов, они будут рассмотрены ниже.<br />
<br />
В '''физическом''' пространстве вводится понятие формы. Объект здесь представлен набором примитивов, объединенных в группы. Каждая из групп примитивов либо связана с одним из твердых тел механического представления (так, что перемещение этого тела в механическом пространстве влечет перемещение соответствующей группы примитивов в физическом), либо ни с чем не связана (такая группа примитивов всегда находится на одном месте: земля, деревья, дома и т.д).<br />
<br />
Под примитивом здесь понимается один из нескольких геометрических примитивов: паралеллепипед, сфера, цилиндр и несколько других.<br />
<br />
В '''графическом''' пространстве, объект представляет собой набор графических примитивов, таких как:<br />
* полигональные поверхностей с различными графическими свойствами (начиная от текстурирования и заканчивая шейдерами),<br />
* источники освещения,<br />
* камеры.<br />
Графическая модель также связана с механическим или физическим представлением — т.е. движок автоматически изменяет положение графических примитивов в соответствии с перемещениями физических.<br />
<br />
Как уже было отмечено выше, объект может быть представлен как во всех трех представлениях, так и не иметь одного или нескольких представлений. Рассмотрим примеры объектов и проанализируем, какие представления они имеют.<br />
<br />
# ''Аватар''. Имеет представления во всех трех пространствах. Является полноценным "физическим" объектом.<br />
# ''Фаербол'', эмиттеры частиц (дым, огонь и т. д) либо другие графические фокусы. Представляются в графическом пространстве.<br />
# ''Поля сил'' как проявления заклинаний или обычного ветра. Представляются в физическом пространстве.<br />
<br />
== Данные объекта ==<br />
<br />
Данные объекта хранятся в [[DISS]] файле в формате [[BXL]]. Объект также может зависеть от других файлов (скрипты, текстуры и др.), ссылки на которые приводятся в основном файле.<br />
<br />
Полный набор свойств объекта, которые можно задать в его файле, достаточно обширен, поэтому сейчас я не буду приводить его полностью — когда этот формат будет окончательно утвержден, будет и документация по нему. В этом разделе я приведу несколько примеров простых объектов с описанием, а в последующих разделах буду приводить информацию о других тегах, если это потребуется.<br />
<br />
Все примеры приведены в формате XML; для конвертирования из XML в BXL существует утилита bxd — на данный момент она реализована на Perl. Для конвертирования объекта, наберите команду<br />
bxd filename.model.xml -f filename.model<br />
где filename.model.xml — имя файла с XML описанием.<br />
<br />
=== Простейший физический объект ===<br />
<br />
Рассмотрим пример файла, описывающего куб размером 1x1x1:<br />
<br />
<source lang="xml" line="1"><br />
<model bxd="http://dao.deeptown.org/bxd/model.bxd"><br />
<material_script node="/media/materials/cube.material" /><br />
<body name="cube"><br />
<geometry><br />
<geom type="box" width="1" height="1" depth="1" /><br />
</geometry><br />
<surfaces><br />
<mesh name="cube" node="/media/meshes/cube_small.mesh" /><br />
</surfaces><br />
</body><br />
</model><br />
</source><br />
<br />
;1-11:Все описание объекта заключено в тег '''model''', у которого нет параметров (кроме стандартной ссылки на BXD-описание для корректного конвертирования в BXL);<br />
;2:Тег '''material_script''' указывает на путь к файлу описания материалов. Формат этого файла описан в [http://www.ogre3d.org/docs/manual/manual_14.html документации OGRE]. Важно отметить, что внутри этого файла, все ссылки на текстуры, шейдеры и пр. делаются также указанием DISS-пути к соответствующему файлу;<br />
;3:Тег '''body''' описывает '''твердое тело''', которое становится частью нашего объекта. В рамках одного объекта таких тел может быть несколько — каждое из них будет описываться отдельным тегом body;<br />
;4:В рамках тега '''geometry''' содержится информация о геометрических примитивах, связанных с данным твердым телом, т.е. о его физическом представлении;<br />
;5:Куб в пространстве легко описывается примитивом '''box''' — паралеллепипедом с указанными шириной, высотой и глубиной (оси x, y и z соответственно). Обратите внимание, что по-умолчанию примитив находится в центре локальных координат данного твердого тела. Сместить его можно тегом '''position''', следующим образом:<br />
<source lang="xml"><br />
<geom type="box" width="1" height="1" depth="1"><br />
<position x="1" y="0" z="0" /><br />
</geom><br />
</source><br />
:Такая конструкция сместила бы примитив на 1 (метр) по оси X относительно локальных координат твердого тела. Аналогично, тегом '''rotation''' с параметрами qw, qx, qy и qz можно задать кватернион вращения данного примитива относительно локальной системы координат.<br />
;7-9:В рамках тега '''surfaces''' задаются графические примитивы, связанные с объектом. В нашем случае такой примитив только один: тег '''node''' указывает имя файла с графическим описанием куба в формате OGRE Mesh (конвертеры в этот формат можно скачать с [http://www.ogre3d.org официального сайта OGRE], там также имеется [http://www.ogre3d.org/wiki/index.php/OGRE_DCC_Tools набор инструментов] для создания графического контента).<br />
<br />
Еще раз обращаю Ваше внимание на то, что физическая и графическая модели объекта хоть и связаны, но разделены. Например, если бы мы в теге geometry указали бы в качестве примитива не куб, а шар, — данный объект все равно выглядел бы кубом (потому что графическое представление мы бы не поменяли), но вел бы себя с точки зрения физики как шар (мог бы покатиться по земле, например).<br />
<br />
И еще один важный момент. Поскольку в данном описании нигде не указаны механические параметры куба, в механическом пространстве этот куб не представлен. Таким образом, он всегда будет статичен в пространстве и не сможет сдвинуться с места (если только его не сдвинуть скриптом).<br />
<br />
=== Более сложный пример ===<br />
<br />
Вот пример более сложной модели куба:<br />
<br />
<source lang="xml" line="1"><br />
<model bxd="http://dao.deeptown.org/bxd/model.bxd"><br />
<material_script node="/media/materials/cube.material" /><br />
<body name="cube"><br />
<mass_data><br />
<mass value="1" /><br />
</mass_data><br />
<geometry><br />
<geom type="box" width="1" height="1" depth="1" /><br />
</geometry><br />
<surfaces><br />
<mesh name="cube" node="/media/meshes/cube_small.mesh" /><br />
</surfaces><br />
</body><br />
<externals><br />
<event_receiver url="gide:/media/scripts/cube_small_dynamic.gbc!CubeSmallDynamic" /><br />
</externals><br />
</model><br />
</source><br />
<br />
Рассмотрим теги, которые были добавлены в этот файл:<br />
;4-6:Тег '''mass_data''' определяет механическое представление тела. В нем можно задать массу (тег '''mass''') и матрицу инерции (не указанный здесь тег '''inertia'''), которая по-умолчанию является единичной матрицой.<br />
;14-16:В теге '''externals''' приводятся дополнительные данные объекта. В данном случае, это тег '''event_receiver''', который определяет обработчик события.<br />
<br />
Обработчик события, как и многое другое, задается при помощи URL. Под URL всегда понимается некоторая строка, включающая в себя тип сущности (в данном случае — обработчика) и адрес этой сущности, который зависит от ее типа. Тип и адрес разделены двоеточием.<br />
<br />
Единственный на данный момент реализованный обработчик событий имеет тип gide. Этот обработчик передает все события некоторому объекту Gide-класса. В адресе указано имя файла скрипта (в DISS) и имя класса, разделенные символом "!".</div>Raw mathttp://man.deeptown.org/index.php/%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D1%81%D0%BB%D0%BE%D0%B2%D0%B8%D0%B5Предисловие2013-07-13T11:56:48Z<p>Raw mat: Откат правок DanielBell91 (обсуждение) к версии Korvin</p>
<hr />
<div>__NOTOC__<br />
В последнее время наблюдается постоянный интерес программистов к написанию собственных языков программирования. Одни видят в этом хороший способ повысить свою квалификацию, другие делают это из интереса, третьи — из желания сделать язык, отвечающий их личному пониманию того, каким же должен быть Идеальный Язык Программирования. Так или иначе, и те и другие пытаются найти компромисс между теми знаниями о языках которые были у них на момент начала работы и того что они ожидали получить в итоге. <br />
<br />
Некоторые программисты пытаются сделать своего рода конгломерат мыслей и идей, объединив в одном языке лучшие черты нескольких других; другие же пытаются создать что-то принципиально новое, не похожее ни на один из существующих языков программирования. Иногда это удается, тогда язык может стать новым словом в программировании и в понимании самого процесса написания программы. Таким примером может послужить идея "графического" или "визуального" программирования. В этом случае программист не пишет программу в обычном понимании этого слова, а "рисует" ее, так же как художник пишет картину. Подобно тому, как художник берет краски с палитры и переносит их на холст, программист, пишущий на визуальном языке, работает с палитрой компонентов, перенося их на рабочее поле, соединяя их связями и тем самым получая готовый Образ программы. Такой подход дает существенный выигрыш в том плане, что мозг программиста работает в естественной для него среде — среде образов, объектов и взаимосвязей, что, в конечном счете, положительно сказывается на эффективности работы программиста и на сокращении трудозатрат, необходимых на получение качественной программы. Конечно, многие могут оспорить это заявление и привести тысячу доводов в защиту "классического" подхода, при котором программист оперирует только текстовым редактором и, конечно, собственным воображением. Каждая задача, каждая проблема может быть решена несколькими путями, с использованием различных инструментов. Какие из них более эффективные или удобные, решать все же самому программисту. <br />
<br />
Однако такие нововведения случаются сравнительно редко. Гораздо чаще происходит своеобразное "изобретение велосипеда", при котором новоиспеченный язык программирования не претендует на место таких "монстров" как С++ или Perl. Взамен, он решает некоторые конкретные проблемы, предлагает свои решения по обеспечению удобства написания кода и его последующей отладки.<br />
<br />
В этом смысле, язык К++ не является исключением. При написании его мы не старались сделать что-то принципиально новое, а скорее хотели переосмыслить наше понимание языков программирования. Мы хотели сохранить лучшие черты, которые мы нашли в других языках и в то же время постараться органично совместить их в нашем языке. Совместить так, чтобы они не противоречили друг другу и представляли собой цельную и законченную систему. В целом, это было довольно сложно, поскольку исходные языки представляли собой принципиально различные системы, "исповедующие" разные идеологии. <br />
<br />
Язык К++ вобрал в себя строгость Паскаля, лаконичность С++, удобство Ruby, гибкость Perl и в чем-то даже "совершенство в простоте", присущее языку Smalltalk. На самом деле, этот список можно продолжить и дальше, однако наибольшее влияние оказали именно вышеперечисленные языки. Если вы знаете один или несколько из этих языков, то по ходу изложения материала вы, скорее всего, будете подмечать знакомые места. Разумеется, чем больше вы знакомы с программированием на этих языках, тем легче вам будет освоить язык К++. <br />
<br />
Надеюсь, уважаемый Читатель еще не был утомлен потоком моего красноречия. Если это все-таки случилось, то вы можете вздохнуть спокойно, потому что самая философская часть книги закончилась. В будущем "воды" будет меньше, однако мы все же постараемся сохранить доверительный стиль изложения и не свалиться в пучину бездны так называемого сухого научного стиля...<br />
<br />
== Роль K++ в проекте Диптаун ==<br />
<br />
На этот язык была возложена большая ответственность. Фактически, он является вторым языком после С++, который будет использоваться в повседневной работе разработчика платформы Диптауна. К++ будет применяться практически во всех сферах, начиная от пользовательского интерфейса и заканчивая объектами виртуального пространства (программирование ботов и аватаров, ai и др.). Отдельно стоит подчеркнуть задачу динамической генерации моделей и текстур, что, в конечном счете, позволит использовать Диптаун даже пользователям, обладающим медленным сетевым соединением (вплоть до модемного). Можно даже сказать, что этот язык является основным языком программирования под платформу Диптауна, поскольку С++ подразумевает решение задач по расширению и интеграции самой платформы в отличие от К++, который используется непосредственно для ее программирования.<br />
<br />
== Почему именно K++? ==<br />
<br />
Как это часто бывает, как идея, так и название языка пришли случайно. Первоначально так назывался экспериментальный язык одного из авторов, названный по первой букве сетевого псевдонима (или по первой букве фамилии). Плюсы указывали на некоторое сходство с С++ (с точки зрения синтаксиса и объектной природы). Затем, после разработки виртуальной машины gide, потребовался язык высокого уровня, который мог бы компилироваться в gide код. Необходимо было либо адаптировать уже существующий язык к нашим нуждам, либо написать свой. По некоторым причинам решили писать свой язык (в основном из соображений единства и целостности кода). Так вспомнили о старой разработке, которая и легла в основу данного языка. Название (раньше просто рабочее обозначение) постепенно закрепилось и заняло свое место окончательно.<br />
<br />
'''Примечание:''' Более подробно история создания языка описана [[История создания языка|здесь]].<br />
<br />
== Для кого эта книга ==<br />
<br />
Эта книга написана в первую очередь для тех, кто желает использовать язык К++. Книга предназначена в первую очередь для обучения собственно языку (синтаксису, принципам программирования и т.д.). Однако в ней так же будут затронуты вопросы решения конкретных практических задач, как например программирование аватаров или написание модулей для графического интерфейса системы. После прочтения этой книги вы сможете писать программы любой сложности, начиная от простейших скриптов и заканчивая сложными программами, использующими все возможности платформы Диптаун. Мы постараемся затронуть как можно больше вопросов связанных с использованием платформы. Будут рассмотрены типовые задачи и приемы их решения. <br />
<br />
Книга ориентирована на подготовленного читателя; предполагается, что читатель уже знаком с программированием вообще и с объектно-ориентированным программированием в частности, обладает навыками написания программ и некоторыми специальными знаниями.<br />
<br />
== Стиль изложения материала ==<br />
<br />
Если вы дочитали до этого места, то уже должны примерно себе представлять, в каком стиле написана книга. Тем не мене, стоит отметить, что авторы всячески будут стараться писать более живым языком, кое-где отходя от основной мысли и разбавляя сухой текст рассуждениями, историческими сведениями и др.<br />
<br />
Авторами учитывался тот факт, что книга писалась в первую очередь как онлайн-руководство; ее структура была изначально рассчитана на использование перекрестных ссылок. Везде, где это возможно, будут проставляться ссылки на другие разделы книги, а так же на дополнительные материалы, касающиеся темы изложения. Сам материал излагается в несколько этапов. Сначала дается краткий обзор темы, затем более детальное рассмотрение вопросов по отдельности. Было решено, что такой подход является более эффективным, нежели обычное, линейное изложение материала. Таким образом, книгу можно использовать и как учебник, и как справочное руководство, а читать ее можно практически с любого интересующего места.<br />
<br />
== Тесная взаимосвязь с gide ==<br />
<br />
Как уже отмечалось выше, язык К++ является высокоуровневым языком, компилирующимся в байт код [[gide]]. Хорошее понимание процессов, происходящих при компиляции, и умение читать листинги gide ассемблера позволят вам писать более эффективные программы, а так же задействовать все возможности виртуальной машины. <br />
<br />
Поэтому, по ходу изложения материала, в некоторых местах будут приводиться листинги как программ, написанных на К++, так и соответствующий им gide код. Это поможет более полно представлять себе, что же происходит на самом деле. Если же вы считаете, что вы еще не достаточно квалифицированны для работы с ассемблером, то смело можете пропускать эти части. Однако настоятельно рекомендуется впоследствии изучить и эти материалы.<br />
<br />
== Принятые обозначения ==<br />
<br />
Рассмотрим кратко типографские обозначения, принятые в книге. При ее написании, авторы старались придерживаться единого стиля, что должно было обеспечить лучшую читаемость материала и более легкое его восприятие.<br />
<br />
Важные места, на которые следует обратить внимание, выделяются '''жирным''' шрифтом, например, так выделяются примечания. Различные термины и определения выделяются ''курсивом'', таким же образом выделяются переменные в ходе описания их использования в тексте (а не в самой программе). Ключевые слова, являющиеся важной частью любого языка и несущие строго определенный смысл, выделяются <tt>'''жирным моноширинным'''</tt> стилем. <br />
<br />
Имена полей функций и методов, названия классов и стандартных типов данных выделяются <tt>моноширинным</tt> стилем. Если описываемый класс имеет свою страницу в данной книге или в другом месте, на него будет сделана ссылка. Имена методов записываются вместе с круглыми скобками позади них, чтобы их можно было визуально отличить от свойств.<br />
<br />
Кроме того, листинги программ записываются отдельно, причем текст программы подсвечивается так, как это принято в редакторе. Подсветка синтаксиса облегчает восприятие программы и позволяет легче выделять отдельные ее структурные элементы. Относительно длинные листинги программ будут дополняться столбцом номеров строк, для облегчения описания действия отдельных частей программы.<br />
<br />
Ниже приведен небольшой пример применения вышеописанных стилей (на самом деле, это вырезка из главы [[Константы]]).<br />
<br />
<br />
----<br />
<br />
<br />
...с точки зрения языка, ''константы'' это те же [[переменные]], за исключением того, что однажды установленное их значение нельзя изменять. В остальном они ведут себя так же как обычные [[объекты]]: их можно использовать в арифметических выражениях, передавать в качестве [[Функции#Аргументы|параметров]] [[Функции|функциям]], использовать их [[K++ как объектно-ориентированный язык#Свойства|свойства]] и вызывать [[K++ как объектно-ориентированный язык#Методы|методы]]. Разумеется, только те, которые не изменяют состояние самого объекта.<br />
<br />
Объявление любой константы начинается с ключевого слова <tt>'''const'''</tt>, за которым идут тип (можно не указывать), [[идентификатор]] имени константы и выражение-инициализатор. <br />
<br />
Вот примеры объявления констант:<br />
<source lang="kpp"> <br />
const real e = 2.7182818;<br />
const pi = 3.1415926;<br />
const s = "hello world";<br />
</source><br />
<br />
Тип константы можно либо указать явно, как это происходит в случае константы ''e'', либо неявно. При этом он будет назначен как тип инициализатора: константа ''pi'' будет иметь тип числа с плавающей точкой (<tt>[[Стандартные типы данных#Числа с плавающей точкой|real]]</tt>), а константа ''s'' — строковый тип (<tt>[[Стандартные типы данных#Строки|string]]</tt>).<br />
<br />
'''Примечание:''' При объявлении констант следите за тем, чтобы их тип был ясно виден из строки объявления. Если выражение инициализатора представляет собой довольно сложное математическое выражение, либо содержит вызовы функций, лучше всего указать тип константы явно (как в примере с константой ''e''). Иначе, это может вызывать проблемы в будущем, при работе программы или при ее отладке. В целом ситуация такая же, что и с инициализацией переменных ([[Переменные#Типизация при инициализации|см. примечание]]).</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5О проекте2013-07-13T11:54:16Z<p>Raw mat: </p>
<hr />
<div>== Для кого эта wiki ==<br />
<br />
Наконец-то, после долгих лет разработки, проект [http://www.deeptown.org/ Deeptown] вплотную подобрался к этапу открытого тестирования. До этого момента разработка проекта велась в основном "вглубь" — мы проектировали основные технологии и писали реализацию их основ — т.е. скелет проекта. Теперь же, когда ядро платформы почти готово и, хоть и со скрипом, но работает, можно приступать к разработке приложений на его основе.<br />
<br />
Горизонты тут открываются достаточно широкие: начиная от драйверов хранения данных DISS и поддержки различных протоколов передачи, и заканчивая всевозможными приложениями на платформе Gide (пользовательский интерфейс, управление объектами, процедурные текстуры и многое другое).<br />
<br />
Очевидно, что нашей относительно небольшой команде разработчиков не справиться со всем этим в одиночку. К тому же, платформа для того и разрабатывалась, чтобы в дальнейшем разрабатывать на ее основе приложения; мы хотим предоставить эту возможность как можно раньше.<br />
<br />
Поэтому мы запустили ряд сервисов для тех, кто хотел бы участвовать в расширении платформы Диптауна, и в создании приложений на ее основе. Эта wiki является отправной точкой для доступа к этим сервисам. На ней также будет вестись разработка документации к различным технологиям и средствам разработки в рамках платформы Диптауна.<br />
<br />
Остальные сервисы включают в себя:<br />
* [[Deeptown SDK|Deeptown Software Development Kit]] — набор программ и утилит для разработки системных и пользовательских приложений;<br />
* [[Багтрекер]] — для отчетов об ошибках;<br />
* [[Рассылка|Почтовая рассылка]] — включает в себя два списка рассылки: рассылку новостей и рассылку для обратной связи с разработчиками.<br />
<br />
== Участие ==<br />
<br />
Проект в целом и эта wiki в частности, подразумевают коллективную работу сообщества. Любой желающий может помочь проекту либо непосредственным участием в делах группы разработчиков, либо как вебмастер - установив на своем сайте ссылку на наш проект, либо косвенно, как посетитель этого сайта. Как вы уже могли заметить, на сайте и на wiki представлено большое количество материалов. Однако мы не в силах всецело контролировать качество публикуемых текстов, хотя и стремимся к этому.<br />
<br />
Вы можете помочь проекту даже не будучи программистом или дизайнером. Для этого достаточно просто быть грамотным человеком. Если на страницах этой wiki вы встретите неточности, орфографические или пунктуационные ошибки, или просто заметите дефиc там, где должно стоять тире — исправьте, пожалуйста, текст так, как вы считаете нужным. Это можно сделать с помощью вкладки "правка" вверху каждой страницы.<br />
<br />
Если вы сомневаетесь относительно верности вашего исправления, напишите об этом на странице обсуждения. Не забудьте только указать место в тексте, где, по вашему мнению, находится ошибка, и ваш вариант её исправления. Ссылка на страницу обсуждения также находится вверху каждой страницы.<br />
<br />
== Некоторые понятия == <br />
Наше название: '''Проект «Диптаун»'''<br><br />
По-английски: '''«Deeptown project»''' <br><br />
Слово «Deeptown» принято переводить как '''«Город-в-Глубине»''' <br><br />
<br><br />
Наш официальный сайт:<br> [http://www.deeptown.org www.deeptown.org]<br><br />
Наш официальный баннер:<br> [[Изображение:Deep_1_88x31.gif|Официальный баннер для сайта 'deeptown.org']]</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D0%B8%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B9Обработка исключений2013-07-13T11:51:57Z<p>Raw mat: </p>
<hr />
<div>Как уже было отмечено во [[Идеология языка#Понятие исключения|введении]], исключения — это мощный механизм, позволяющий в разы сократить время, требуемое на написание программ и значительно повысить их качество. В основном, это достигается за счет того что, программист пишет более простой код, не отвлекаясь от основной задачи на разного рода рутинные операции, вроде обработки ошибок, а значит, его внимание сконцентрировано на самой проблеме. В итоге, это приводит к написанию более качественного кода. В этой главе будет рассмотрена сущность механизма исключений, а так же способы их применения на практике. Будут даны типовые схемы разработки программ и даны соответствующие пояснения.<br />
<br />
== Идеология исключений ==<br />
<br />
С философской точки зрения, ''исключением'' называется некоторое событие, произошедшее при работе программы и несущее негативный характер. Имеется в виду, что это событие не должно происходить при нормальных условиях. Примером таких событий могут послужить следующие ситуации:<br />
* Обрыв линии связи и, как следствие, прекращение передачи<br />
* Внезапный конец файла конфигурации (или БД)<br />
* Ввод данных, не соответствующий требуемому формату (например строки, когда ожидалось число)<br />
* Ошибка выделения памяти<br />
* Падение метеорита, война, нашествие иноплянетян и т. д.<br />
<br />
Все вышеперечисленные события так или иначе могут произойти в ходе работы программы, однако, с точностью предсказать их практически невозможно, можно только оценить вероятность. Хорошая программа так и делает: в тех местах, где могут потенциально произойти исключительные ситуации, вставляется код, проверяющий состояние и производящий некоторые действия по "ликвидации последствий". Например, в случае ошибки связи, программа может попытаться установить соединение заново, в то время как при ошибке внезапного конца файла, остается только уведомить об этом пользователя, показав соответствующее сообщение об ошибке.<br />
<br />
Проблема заключается в том, что не всегда можно угадать место, где может произойти ошибка. Проверять же состояние ошибки буквально в каждой операции — утомительно и практически невозможно. Это засоряет код алгоритма всевозможными вспомогательными конструкциями (условиями проверки на ошибку), делает его менее читаемым и усложняют и без того нелегкую задачу отладки.<br />
<br />
При использовании концепции исключений, все выглядит совсем иначе. Большую часть кода, программист пишет из соображения, что "все в порядке". При этом, код получается кратким и содержит только те действия, ради которых он создавался.<br />
<br />
В некоторых местах, где заранее предусматривается возможность проявления ошибки (например при открытии файла), вставляется специальная конструкция исключения. Она состоит из основного блока и одного или нескольких блоков — перехватчиков исключений. Выглядит эта конструкция так:<br />
<source lang="kpp"><br />
try {<br />
/* try-блок (основное тело) */<br />
} catch (/* переменная для объекта исключения 1 */) {<br />
/* код перехватчика 1 */<br />
} catch (/* переменная для объекта исключения 2 */) {<br />
/* код перехватчика 2 */<br />
}<br />
</source><br />
<br />
Ключевое слово <tt>'''try'''</tt> объявляет начало защищенного блока. Затем, идет основное тело или try-блок, в котором содержатся конструкции, соответствующие нормальной работе. В примере с файлом, здесь будет находиться код чтения содержимого файла и, возможно, его обработки. Если в ходе выполнения этого блока, возникнет исключительная ситуация, то управление будет передано одному из блоков обработки, соответствующему типу возникшего исключения (об этом чуть позже). В круглых скобках указывается имя переменной, которой будет назначен ''объект исключения'' — объект некоторого класса, который был "выброшен" из кода в качестве исключения.<br />
<br />
== Объект исключения ==<br />
<br />
С точки зрения языка К++, объект исключения — это такой же объект (или класс объектов), как и все остальные, только он несет строго определенный смысл, привязанный к концепции исключений. Иначе говоря, объект исключения — это специальная сущность, которая содержит в себе некоторую известную информацию об ошибке. Например, в случае ошибки чтения файла, в объекте может содержаться путь к файлу. В случае ошибки формата исходных данных, он может содержать информацию о том, какие были входные данные и какие ожидались на самом деле. Словом, объект исключения содержит информацию, которая отражает возникшую ошибку. Как правило, все объекты исключения имеют строковое поле, в которое записывается текстовая информация, описывающая проблему. Например, оно может содержать строки вида: "не могу открыть файл", "отказано в доступе" или "так сложились звезды". Впоследствии, эти строки могут быть записаны в лог файл, либо показаны пользователю в виде сообщения.<br />
<br />
== Генерация исключения ==<br />
<br />
Код, который первым обнаруживает исключительную ситуацию (то есть, стоит у ее истоков) и желающий сообщить о ней "наверх", должен создать объект исключения и "выбросить" его. Создается объект так же, как и любой другой объект в языке К++: либо с помощью оператора <tt>'''new'''</tt>, либо с помощью [[Классы и объекты#Конструкторы|конструктора]] (если он предусмотрен). "Выбрасывание" объекта осуществляется с помощью специального оператора <tt>'''throw'''</tt>, который принимает объект в качестве параметра. Обычно, эти операции совмещают в одном действии. <br />
<br />
Приведем пример некоторой функции, которая проверяет правильность передаваемых ей данных и выбрасывает исключение в случае несоответствия:<br />
<source lang="kpp"><br />
function Process(const int idx) {<br />
if (! idx in 5 .. 10)<br />
throw "индекс должен быть в диапазоне от 5 до 10";<br />
/* нормальный код обработки */<br />
}<br />
</source><br />
<br />
В первой строке тела функции, проверяется условие, необходимое функции для работы. Если условие не выполняется, то производится генерация исключения, причем в качестве объекта исключения выступает объект строки. <br />
<br />
На практике применяются специальные классы распространенных исключений, которые объявлены в [[Стандартная библиотека Gide|стандартной библиотеке]]. Таким образом, все языки использующие стандартную библиотеку и умеющие работать с исключениями, смогут успешно взаимодействовать с помощью этого механизма.<br />
<br />
Приведем некоторые из наиболее распространенных классов исключений и поясним их назначение:<br />
<br />
::{| width=60%<br />
| <tt>EInvalidCall</tt> || неверный вызов (входные данные неверны)<br />
|-<br />
| <tt>EFailed</tt> || операция не удалась (и все тут)<br />
|-<br />
| <tt>EAagain</tt> || по тем или иным причинам требуется повтор операции<br />
|-<br />
| <tt>EDenied </tt> || действие запрещено!<br />
|-<br />
| <tt>ECancelled </tt> || операция была отменена (например пользователем)<br />
|-<br />
| <tt>EAlready</tt> || операция уже была выполнена<br />
|-<br />
| <tt>ERangeError</tt> || ошибка диапазона (выход значения за допустимые границы)<br />
|-<br />
| <tt>EDivisionByZero</tt> || арифметическая ошибка деления на ноль<br />
|-<br />
| <tt>ETimeout </tt> || истекло время ожидания чего-либо<br />
|-<br />
| <tt>EStreamError</tt> || ошибка [[потока]]<br />
|-<br />
| <tt>ERegexpError</tt> || ошибка при разборе [[регулярного выражения]]<br />
|-<br />
| <tt>EAbstractError</tt> || попытка вызова [[Классы и объекты#Методы|абстрактного метода]]<br />
|}<br />
<br />
Здесь приведены только наиболее общие описания классов. Более конкретная информация содержится в самом объекте исключения. Классы исключений необходимы для того, чтобы сортировать возникающие ошибки по типам, и на основе этой информации обрабатывать их различным образом.<br />
<br />
Например, в описанном выше случае, наиболее подходящим классом ошибки будет <tt>ERangeError</tt>. Соответственно, код генерации ошибки можно написать так:<br />
<source lang="kpp"><br />
throw ERangeError.create("индекс должен быть в диапазоне от 5 до 10");<br />
</source><br />
<br />
== Перехват исключений ==<br />
<br />
Для обработки возникающих исключений используется конструкция перехвата. Она начинается с ключевого слова <tt>'''catch'''</tt>, следом за которым, в круглых скобках, идет описание переменной исключения, а затем, собственно, блок обработчика. Например, так мог бы выглядеть код вызова вышеописанной функции <tt>Process()</tt> и обработки возникающего исключения, если бы оно имело место:<br />
<source lang=kpp><br />
try {<br />
Process(/*выражение задающее индекс*/);<br />
/* другой код */<br />
} catch (e) {<br />
print("Ошибка обработки индекса. #{e.name}: #{e.description}\n");<br />
/* код обработки исключения */<br />
}<br />
</source><br />
<br />
Для того чтобы сослаться на объект исключения используется переменная ''e''. Все системные классы исключений имеют свойства <tt>name</tt> и <tt>description</tt>. Первое содержит имя исключения, а второе — описание ошибки. <br />
<br />
В зависимости от класса исключения могут добавляться дополнительные поля, более точно характеризующие ошибку. Для того чтобы можно было классифицировать объект исключения по его классу (пардон за каламбур), применяется расширенная форма конструкции перехвата:<br />
<br />
<source lang=kpp><br />
try {<br />
/* защищаемый код */<br />
} catch (/*класс 1*/ /*имя объекта 1*/) {<br />
/* код обработки исключения 1 */<br />
} catch (/*класс 2*/ /*имя объекта 2*/) {<br />
/* код обработки исключения 2 */<br />
...<br />
}<br />
</source><br />
<br />
Таким образом, при объявлении переменной исключения, мы можем указывать ее тип и потом ссылаться на <br />
специфические поля объектов, соответствущих этому типу:<br />
<source lang=kpp><br />
try {<br />
/* защищаемый код */<br />
} catch (EAgain e) {<br />
/* запрос у пользователя на повторение операции */<br />
} catch (ERangeError e) {<br />
/* отображение ошибки */<br />
} catch (e) {<br />
/* запись в лог файл */<br />
}<br />
</source><br />
<br />
В вышеприведенном коде используются три обработчика исключений: первые два перехватывают исключения определенного типа, а третий — все оставшиеся. Структура обработчиков исключений чем-то напоминает конструкцию <tt>'''[[Основные синтаксические конструкции#Оператор множественного выбора (switch)|switch]]'''</tt>.<br />
<br />
'''Примечание 1:''' следует помнить, что правильность разбора исключений по классам, зависит от очередности расположения блоков перехватчиков в конструкции. При перехвате исключения выбор обработчика происходит по принципу первого совпадения, при котором проверяется, имеет ли объект исключения в своей иерархии класс, указанный в перехватчике. Если скажем, перехватчик <tt>'''catch''' (e)</tt> будет указан первым, то он и будет собирать все исключения, поскольку он сработает на любой класс объекта исключения. Вообще, нужно стараться располагать перехватчики по убыванию степени абстракции — от пользовательских классов до системных и далее, до общего перехватчика.<br />
<br />
== Ловить или не ловить? ==<br />
<br />
Блоки перехвата исключений должны вставляться только в тех местах, где вы точно знаете, как надо поступить при возникновении исключения. Если такой уверенности нет — смело пропускайте исключение "мимо ушей". Рано или поздно оно будет словлено там, где программа располагает бо́льшими сведениями относительно дальнейшей модели поведения. <br />
<br />
То есть, не стоит ловить исключение только потому что это можно сделать. Смысл всей технологии в том, что ошибки обрабатываются в тех местах, где их можно исправить. <!--Приведем парочку примеров разных ситуаций, иллюстирующих проблему. Допустим, мы пишем библиотечный код работы с сетевыми соединениями, который представляет собой надстройку над сокетами. В процессе чтения из сокеты мы можем столкнуться с ошибкой чтения.--><br />
== Исключения и работа с ресурсами ==<br />
<br />
Основная проблема при работе со внешними ресурсами заключается в том, что их надо освобождать. Конечно, в нашем случае существует сборщик мусора, который рано или поздно обнаружит "ничейный" объект и инициирует процедуру его удаления. При этом, грамотно написанный unmanaged объект должен произвести все необходимые операции по освобождению ассоциированного с ним ресурса (файла, сетевого соединения, графической сущности и т. д.).<br />
<br />
Однако, может пройти длительное время, прежде чем сборщик мусора "спохватится" и начнет свою работу. В случае небольших программ и того хуже — он может быть вызван всего раз — при завершении работы программы. Таким образом, если программа представляет собой например скрипт, создающий на экране некоторые графические сущности или объекты виртуального пространства, то весь этот "мусор" будет маячить перед глазами до того момента, пока программист не озаботится его удалением. <br />
<br />
Разумеется, грамотно написанная программа должна освобождать ресурсы как можно быстрее (не путать с обычными объектами). Обычно это осуществляется вызовом специального unmanaged метода, который и производит все необходимые манипуляции на низком уровне. Тогда код работы с ресурсом может выглядеть как-то так:<br />
<source lang="kpp"><br />
var resource = MyResource.open("some initial data"); // создаем ресурс<br />
resource.bla(bla); // работаем с ресурсом<br />
foo(resource); <br />
//... <br />
resource.release(); // освобождаем ресурс<br />
</source><br />
<br />
В приведенном выше примере, метод <tt>release()</tt> является финализатором, который освобождает ресурс. Логическая ошибка такого кода в том, что он будет корректно рабоать только в случае нормальной работы программы. В случае если код работы с ресурсом возбудит исключение, управление будет передано "наверх" минуя оставшуюся часть кода, в том числе и оператор финализации. В результате, ассоциированные с этим блоком кода ресурсы не будут освобождены тогда, когда этого хотелось разработчику.<br />
<br />
В принципе, можно попробовать схитрить и обернуть данный код в <tt>'''try'''</tt> блок, а в блоке перехвата исключений проводить принудительную финализацию ресурса. Тогда код будет выглядеть как-то так:<br />
<source lang="kpp"><br />
var resource = MyResource.open("some initial data"); // создаем ресурс<br />
try {<br />
resource.bla(bla); // работаем с ресурсом<br />
foo(resource); <br />
//...<br />
resource.release(); // освобождаем ресурс<br />
} catch (Exception e) {<br />
resource.release(); // освобождаем ресурс<br />
throw e; // вторично выбрасываем исключение<br />
}<br />
</source><br />
<br />
С функциональной точки зрения этот код корректен. Ресурс финализируется в любом случае, вне зависимости от того, каким образом управление покинуло защищаемый блок кода. Однако с точки зрения удобства использования и "прозрачности" такой код не очень хорош. Тело программы перегружается повторяющимися конструкциями, код становится менее читаем; не говорим уже о том, что можно банально забыть вписать строку финализации в обработчик исключения (или забыть изменить ее при смене процедуры финализации). В общем и целом, такой подход производит больше проблем чем решает.<br />
<br />
Для того чтобы сказать компилятору, что некоторый код необходимо выполнить в любом случае, независимо от того что произошло выше, необходимо использовать ключевое слово <tt>'''ensure'''</tt>. Тогда вышеприведенный код примет следующий вид:<br />
<source lang="kpp"><br />
var resource = MyResource.open("some initial data"); // создаем ресурс<br />
try {<br />
resource.bla(bla); // работаем с ресурсом<br />
foo(resource); <br />
// ...<br />
} ensure {<br />
resource.release(); // освобождаем ресурс<br />
}<br />
</source><br />
<br />
Обратите внимание, что код финализации записан всего один раз, отдельно от основного кода. Никаких промежуточных объектов исключения и прочей "шелухи" не наблюдается. Всю работу по организации потока управления компилятор берет на себя. От нас требуется только корректно записать код работы с объектом.<br />
<br />
Конструкция работает следующим образом: сначала вызвается инициализатор ресурса, то есть первая строка. Затем управление передается в защищаемый блок. Если все прошло гладко и исключений выброшено не было, то после защищаемого блока вызывается <tt>'''ensure'''</tt> блок а затем управление передается дальше по коду.<br />
<br />
Если же где то в защищаемом коде было выброшено исключение, управление <em>сперва</em> передается в <tt>'''ensure'''</tt> блок, а потом выброшенное исключение передается "наверх"; то есть управление будет переданно ближайшему <tt>'''catch'''</tt> блоку, способному обработать данный класс исключений. Самое главное заключается в том, что освобождение ресурса гарантируется в любом случае.</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E%D0%B1%D0%B7%D0%BE%D1%80_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BE%D0%B2Обзор операторов2013-07-13T11:50:51Z<p>Raw mat: Откат правок DanielBell91 (обсуждение) к версии Root</p>
<hr />
<div>Как уже [[Классы и объекты#Объектно-ориентированное программирование|говорилось ранее]], язык K++ является полностью объектно-ориентированным языком. Это значит, что в отличие от многих распространенных языков, таких как C, C++ или Паскаль, в нем нет понятия [http://en.wikipedia.org/wiki/Plain_Old_Data_Structures простейших типов данных]. То есть, любая переменная является объектом, и даже любая константа в выражении является скрытым созданием объекта. Так, например, при вычислении значения переменной i в выражении<br />
<source lang="kpp"><br />
var i = "Hello, world!".length();<br />
</source><br />
<br />
создается '''объект''' класса <tt>'''string'''</tt> со значением "Hello, world!", и для него вызывается метод length().<br />
<br />
Рассмотрим другой пример:<br />
<source lang="kpp"><br />
var i = 3+5;<br />
</source><br />
<br />
Что происходит в этом случае?<br />
<br />
Для того, чтобы это понять, обратимся к концепции ООП. В соответствии с этой концепцией, каждый [[Понятие класса|класс]] представляет собой описание данных и методов, работающих с этими данными. Таким образом, для того, чтобы описать класс целых чисел - <tt>'''int'''</tt>, нужно, в часности, определить метод их сложения.<br />
<br />
Для того, чтобы это сделать, можно было бы написать примерно следующий код:<br />
<source lang="kpp"><br />
class int {<br />
public const function int add(const int x)<br />
{<br />
// возвращаем сумму текущего объекта и объекта x<br />
}<br />
}<br />
</source><br />
<br />
В этом случае, для того, чтобы найти сумму двух чисел, нужно было бы написать:<br />
<source lang="kpp"><br />
var i = x1.add(x2);<br />
</source><br />
<br />
или, в предыдущем примере,<br />
<source lang="kpp"><br />
var i = 3.add(5);<br />
</source><br />
<br />
Этот код вполне справляется с поставленной перед ним задачей, но у него есть один большой недостаток: он некрасив и сложно читаем. Язык программирования создается для человека, а человеку гораздо удобнее написать математический оператор сложения "3+5", чем довольно сложную и замысловатую конструкцию "3.add(5)".<br />
<br />
Поэтому, в язык K++ введено специальное понятие - '''оператор'''.<br />
<br />
Как видно из предыдущего примера, оператором сложения может служить метод, принимающий один параметр. В общем случае, ''оператором является метод, имеющий определенное название и удовлетворяющий определенным правилам относительно количества параметров и типа возвращаемого значения''.<br />
<br />
Более подробно о различных типах операторов и о способе их объявления и перезагрузки будет рассказано в данном разделе книги. Здесь же следует отметить несколько важных моментов.<br />
<br />
Поскольку оператор является методом, то и правила вызова оператора такие же, как правила вызова метода. Таким образом,<br />
* аргументы, передаваемые оператору, автоматически преобразуются к соответствующим типам, если это необходимо;<br />
* если объект, для которого вызывается оператор, имеет динамический тип, то для вызова оператора будет сгенерирован динамический код.<br />
<br />
Первое из этих правил следует обсудить отдельно.<br />
<br />
Рассмотрим следующий код:<br />
<source lang="kpp"><br />
var i1 = 2 + '3';<br />
var i2 = '3' + 2;<br />
</source><br />
<br />
Какие типы и значения будут у переменных i1 и i2 в результате выполнения этого кода?<br />
<br />
В первой строке, для объекта 2, имеющего тип int, вызывается оператор + с аргументом '3', имеющим тип string. При этом, аргумент преобразуется к типу int - и результат имеет тип int и значение 2+3 = 5.<br />
<br />
Во второй строке, для объекта '3', имеющего тип string, вызывается оператор + (конкатенация строк) с аргументом 2, имеющим тип int. Аргумент преобразуется к типу string ('2') - и результат имеет тип string и значение '3' + '2' = '32'.</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E%D0%B1%D1%8A%D1%8F%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D1%85_%D0%B8_%D0%BA%D0%BE%D0%BD%D1%81%D1%82%D0%B0%D0%BD%D1%82Объявление переменных и констант2013-07-13T11:49:44Z<p>Raw mat: </p>
<hr />
<div>Про объявление как переменных так и констант уже достаточно много говорилось в предыдущих главах этой книги. Вероятно, Читатель уже хорошо себе представляет что такое [[переменные]], чем они отличаются от [[Константы|констант]] и как следует их [[Переменные#Типизация при инициализации|инициализировать]]. Читатель уже должен представлять себе, что такое [[Переменные#Понятие переменной, тип переменной|тип]] переменной или константы, и чем статически типированные переменные отличаются от нетипированных, [[Переменные#Нетипированные (динамические) переменные|динамических переменных]]. В этой главе мы не будем уделять много внимания философским вопросам, касающимся переменных и констант, а опишем только сами синтаксические конструкции объявления.<br />
<br />
Вот несколько примеров объявления как одиночной переменной, так и группы переменных в рамках одного оператора:<br />
<br />
<source lang="kpp" line="1"><br />
var x;<br />
var x = "hello";<br />
var x, y;<br />
var x, y = 0;<br />
var int x, y;<br />
var int x, real y;<br />
var int x = "1", y = 1.5;<br />
</source><br />
<br />
При объявлении переменных важно понимать, что при явном указании типа переменной, он сопостовляется только идентификатору идущему сразу после него. Таким образом, строка 5 объявляет переменную ''x'' типа <tt>[[Стандартные типы данных#Целые числа|int]]</tt>, в то время как переменная ''y'' будет иметь [[Переменные#Нетипированные (динамические) переменные|динамический тип]]. <br />
<br />
То же самое относится и к инициализаторам, которые ставятся в соответствие только той переменной, которая стоит слева от оператора "=". К примеру, выражение в строке 4 будет инициализировать только переменную ''y'', в то время как переменная ''x'' не будет иметь инициализатора; то есть для переменной ''x'' объявление эквивалентно тому что записано в строке 1.<br />
<br />
Обратите внимание на строку 7: переменная ''x'' имеет инициализатор типа <tt>string</tt>, но ввиду того что тип переменной указан явным образом, переменная будет иметь тип <tt>int</tt>. При этом, тип инициализатора будет ''приведен'' к типу переменной. Если же типы переменной и инициализатора ''неприводимы'', — будет выдано сообщение об ошибке.<br />
<br />
Приведем еще раз те же самые примеры, но оформим их в виде таблицы, дополнив сведениями о типах переменых:<br />
<br />
<source lang="kpp"><br />
//Строка объявления Тип x Тип y Инициализатор x Инициализатор y<br />
<br />
var x; динамика <br />
var x = "hello"; string "hello" <br />
var x, y; динамика динамика<br />
var x, y = 0; динамика int 0<br />
var int x, y; int динамика <br />
var int x, real y; int real<br />
var int x = "1", y = 1.5; int real "1" as int 1.5<br />
</source><br />
<br />
'''Примечание:''' как видно из таблицы, существует различие между инициализацией переменной и присваиванием ей значения, которое заключается в том, что в зависимости от условий, тип переменной может оказаться либо динамическим, либо фиксированным статическим; внешне же, выражения вызывающие такое поведение, могут выглядеть очень похоже. Рассмотрим следующий пример:<br />
<br />
<source lang="kpp"><br />
var x = 0;<br />
var y;<br />
y = 0;<br />
</source><br />
<br />
В результате выполнения этого кода и переменная ''x'', и переменная ''y'' будут иметь значение равное 0. Более того, типы переменных в этот момент будут совпадать и равны <tt>int</tt>. Однако, переменная ''x'' будет иметь статический тип, в то время как переменная ''y'' — [[Переменные#Нетипированные (динамические) переменные|динамический]]. Если же мы попытаемся присвоить этим переменным другие значения, заведомо неприводимого типа, то произойдет следующее:<br />
<br />
<source lang="kpp"><br />
x = { |x| return x + 1; };<br />
y = [1, 2, 3];<br />
</source><br />
<br />
В случае переменной ''x'' будет сгенерирована ошибка еще на этапе компиляции, поскольку тип переменной был назначен статически при инициализации и не может быть изменен в ходе работы программы. В случае переменной ''y'' никакой ошибки не произойдет, поскольку это динамическая переменная. В ходе присваивания, она просто изменит свой тип на <tt>[[Стандартные типы данных#Массивы и списки|array]]</tt> и будет представлять собой уже массив из трех чисел.<br />
<br />
----<br />
<br />
<br />
Объявления констант вместо <tt>'''var'''</tt>, начинаются с ключевого слова <tt>'''const'''</tt> и синтаксически ничем не отличаются от объявления переменных, за исключением того, что константы обязательно должны инициализироваться:<br />
<br />
<source lang="kpp"><br />
const x; //неверно. требуется инициализатор<br />
const x = "hello"; //верно<br />
const x, y = 0; //неверно. константа x не инициализируется<br />
const int x = "1", y = 1.5; //верно<br />
</source><br />
<br />
== Конструкция группового объявления ==<br />
<br />
Существует также специальная конструкция, позволяющая объявить серию переменных и одновременно присвоить им значения из массива. Выглядит это следующим образом:<br />
<br />
<source lang="kpp"><br />
var (x, y, z) = [1, '2', true];<br />
</source><br />
<br />
Будут объявлены три динамических переменных, инициализованные соответствующими элементами из массива. Размер массива должен быть не меньше количества объявляемых переменных. Если это не так, то переменные, которым не хватило элементов массива, не будут инициализированы.<br />
<br />
Использование группового объявления особенно удобно при работе с функциями, возвращающими массив в качестве значения. Допустим, у нас есть операция, по логике работы возвращающая набор объектов. Такой результат можно описать в виде класса, полями которого будут значиться возвращаемые результаты. Тогда функция создает объект результат, устанавливает ему соответствующие значения свойств и возвращает его. В коде вызывающем функцию производится обратная операция, а именно заводится переменная под объект-результат и далее используется в коде обработки. В таком случае класс объекта-результата работает как структуры в С++. <br />
<br />
Пример использования:<br />
<source lang="kpp"><br />
class QueryResult {<br />
var FName, FAddress, FPhone;<br />
public:<br />
property string name read FName;<br />
property string address read FAddresss;<br />
property string phoneNumber read FPhone;<br />
operator new(_name, _addr, _phone) { <br />
(FName, FAddress, FPhone) = [_name, _addr, _phone]; <br />
}<br />
}<br />
<br />
function SearchByName(const string Name) {<br />
// ... код обращения к БД ...<br />
// предположим, были возвращены следующие данные:<br />
var result = new QueryResult( 'Vassily Pupkin', <br />
'Бобруйская область, село Урюпинское, д. 13', '223-322');<br />
return result; <br />
}<br />
<br />
function main() {<br />
// ...<br />
var result = SearchByName('%Pupkin%'); // делаем запрос<br />
// работаем с результатами, используя result.name, result.address и result.phoneNumber<br />
puts("Телефон Васи Пупкина: #{result.phoneNumber}");<br />
}<br />
</source><br />
<br />
В принципе подобный код вполне имеет право на существование, однако далеко не всегда удобно и эффективно городить огород из классов, только ради удобной передачи данных. Особенно если данный класс используется в единственном месте программы.<br />
<br />
С использованием конструкции группового объявления, задача передачи и разбора параметров становится намного проще:<br />
<source lang="kpp"><br />
function SearchByName(const string Name) {<br />
// ... код обращения к БД ...<br />
// ... 'Vassily Pupkin', 'Бобруйская область, село Урюпинское, д. 13', '223-322'<br />
return [ name, addr, phone ]; <br />
}<br />
<br />
function main() {<br />
// ...<br />
var (name, addr, phone) = SearchByName('%Pupkin%'); // делаем запрос <br />
puts("Телефон Васи Пупкина: #{phone}");<br />
}<br />
</source><br />
<br />
Функция, которой необходимо вернуть несколько значений оборачивает их в массив прямо по месту и возвращает одним объектом. В точке вызова производится обратная операция по вытаскиванию значений из массива и размещению в отдельных переменных.<br />
<br />
<br />
А вот простой способ разделить входную строку на подстроки и назначить каждой из них свою переменную:<br />
<source lang="kpp"><br />
var record = System.shellExecute("head -1 /etc/passwd"); // строка вида 'root:x:0:0:root:/root:/bin/bash'<br />
var (account, password, UID, GID, info, homedir, shell) = record.split(':');<br />
puts("Пользователь #{account} с номером #{UID} пользуется оболочкой #{shell} и хранит свои данные в #{homedir}");<br />
</source><br />
<br />
Метод [[string#split]] возвращает массив подстрок, поэтому зная результат выполнения метода мы можем произвести нашу операцию объявления переменных. Это намного удобнее, чем обращаться к элементам по индексам, тем самым избегая ошибок и опять же делая код более читаемым.<br />
<br />
'''Примечание: ''' Существует также похожий синтаксис для оператора присваивания. Смысл точно такой же, как и у оператора объявления, только используются созданные ранее переменные. То есть, объявления переменных не происходит:<br />
<br />
<source lang="kpp"><br />
var x = 1, y = 2; // объявление переменных<br />
(x, y) = [3, 4]; // групповое присваивание<br />
(x, y) = [y, x]; // простой способ обменять значения местами<br />
</source><br />
<br />
== Смотри также ==</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BB%D0%B0%D1%82%D1%84%D0%BE%D1%80%D0%BC%D1%8B_GideОписание платформы Gide2013-07-13T11:46:25Z<p>Raw mat: </p>
<hr />
<div>== Введение ==<br />
<br />
=== Роль скриптов в проекте ===<br />
<br />
Многие объекты виртуального мира обладают некоторым специфичным поведением. Например, при нажатии на кнопку зажигается лампочка, а для открытия некоторых дверей требуется ключ (пароль). Для того, чтобы задать специфичное поведение объекту, предусмотрено два способа:<br />
<br />
# написание плагина к физическому движку Диптауна, расширяющего стандартное дерево классов объектов;<br />
# написание скрипта на специализированном языке программирования.<br />
<br />
Еще одна важная область применения скриптов - т.н. процедурные модели и текстуры. Суть их заключается в следующем.<br />
<br />
Как известно, классически текстура задается в виде массива чисел, каждое из которых определяет цвет соответствующей точки. Даже при достаточно хорошем сжатии, текстуры имеют большой размер и их очень "дорого" передавать по сети. Альтернативой являются процедурные текстуры. Процедурная текстура задается некоторой программой (скриптом), которая рисует изображение. При этом по сети передается относительно маленькая программа, которая затем исполняется на машине пользователя, а полученное изображение используется в качестве текстуры. <br />
<br />
Аналогично, процедурная модель - это скрипт, создающий трехмерную модель. <br />
<br />
Итак, скрипты в проекте используются, в основном, в трех направлениях: <br />
* задание поведения объектов; <br />
* создание процедурных моделей; <br />
* создание процедурных текстур.<br />
<br />
=== Терминология ===<br />
<br />
В этом документе будут использованы следующие термины: <br />
* Gide (гайд) - единый низкоуровневый язык; <br />
* байт-код gide - бинарный код, получаемый после компиляции кода gide; <br />
* виртуальная машина gide - программа, исполняющая байт-код. <br />
<br />
=== Основные концепции ===<br />
<br />
Для различных задач удобны различные языки и различные наборы функций. Поэтому, во избежание повторного написания низкоуровневых библиотек, а также для удобства программирования скриптовых языков, было решено разработать единый низкоуровневый язык - gide. Код любого другого языка компилируется в код gide, затем компилятор gide создает платформенно-независимый байт-код, который исполняется виртуальной машиной gide. Механизм компиляции и исполнения кода изображен на рис. 1. <br />
<br />
[[Image:gidebc-1.gif|thumb|Рис. 1. Механизм компиляции и исполнения кода]]<br />
<br />
Преимущества такого подхода очевидны: <br />
* использование библиотек одного языка другими; <br />
* единая виртуальная машина для всех языков. <br />
<br />
Язык gide берет за основу модульную архитектуру. Модули gide - это динамически подключаемые библиотеки, которые бывают двух типов: <br />
# библиотеки, представляющие собой байт-код gide ('''управляемые''' модули);<br />
# "обычные" динамически-подключаемые библиотеки ('''неуправляемые''' модули).<br />
<br />
Любые из этих библиотек можно подключать как статически, так и динамически.<br />
Область задач gide:<br />
* расчет процедурных текстур, объектов и, возможно, звуков;<br />
* управление поведением объектов в виртуальном пространстве; <br />
* управление объектами интерфейса на сервере интерфейса.<br />
<br />
Gide должен предоставлять максимально удобный механизм управления "внешними" объектами. Набор таких объектов определяет набор решаемых задач, т.е. некий контекст того или иного скрипта.<br />
<br />
=== Модель языка ===<br />
<br />
Основополагающим понятием в модели Gide является объект.<br />
<br />
'''Объект''' - это некоторая абстрактная сущность, для которой определены свойства (поля) и операции (методы). Свойства объекта также являются (именованными) ссылками на объекты. И набор свойств, и набор операций - это динамические наборы, т.е. наличие того или иного свойства/операции определяется в момент выполнения, а не в момент компиляции. Gide позволяет оперировать ссылками на объекты. То есть единственный существующий в нем тип данных - это ссылка на объект. Ссылка может либо указывать на объект, либо быть равной 0.<br />
<br />
Набор методов объекта определяется его классом.<br />
<br />
'''Класс''' - это набор функций, каждая из которых имеет доступ к объекту, для которого она была вызвана. У класса может быть один или несколько '''родительских''' классов: при поиске метода, он ищется сначала в классе объекта, а затем - рекурсивно - во всех родительских классах, в порядке их перечисления.<br />
<br />
'''Функция''', в свою очередь - это некоторый набор операторов, выполняющих определенные действия и составляющие '''тело функции'''. Функция может быть методом какого-либо класса, либо отдельной функцией, не являющейся методом. Функция может принимать некоторое количество параметров и возвращать результат (объект).<br />
<br />
Набор классов, предоставляемых скрипту, формирует так называемый контекст исполнения, т.е. по сути стандартную библиотеку языка.<br />
<br />
Эта библиотека, для тех или иных вариантов использования, может включать в себя:<br />
* объекты стандартных типов данных:<br />
** целые числа,<br />
** вещественные числа,<br />
** строки,<br />
** идентификаторы объектов,<br />
** указатели,<br />
** массивы;<br />
* стандартные операции для этих объектов:<br />
** арифметические операции,<br />
** операции сравнения,<br />
** математические функции,<br />
** и пр.<br />
<br />
== Синтаксис языка gide ==<br />
<br />
Синтаксис языка Gide максимально упрощен - с рассчетом на то, что поверх него будут писаться реализации языков более высокого уровня.<br />
<br />
Каждая непустая строка исходного кода gide - это некоторая инструкция, записанная в следующей форме:<br />
ключевое_слово аргумент1, аргумент2, ..., аргументN<br />
<br />
Некоторые инструкции допустимы только за пределами тела функции, некоторые, наоборот, допустимы только внутри тела функции.<br />
<br />
Аргументы для инструкции - это произвольные текстовые строки. Если значение аргумента содержит управляющие символы (пробел, табуляция, перевод строки, запятая, символы # или ") - его необходимо заключить в двойные кавычки. Внутри таких кавычек также допустимы escape-последовательности \r, \n, \t, \" и \\.<br />
<br />
'''ЗАМЕЧАНИЕ''': вставка произвольных символов \xNN на данный момент не поддерживается, но запланирована на будущее.<br />
<br />
Весь текст от символа # и до конца строки - комментарий, он игнорируется.<br />
<br />
Внутри тела функции также возможно вставлять т.н. '''метки'''. Метка - это некоторая уникальная в пределах данной функции ссылка на инструкцию. Метка выглядит следующим образом:<br />
имя_метки:<br />
<br />
Она может быть записана как на отдельной строке, так и на строке с инструкцией.<br />
<br />
=== Подключение внешних модулей ===<br />
<br />
Для использования классов и функций, объявленных во внешних модулях (любого типа), необходимо их подключить, используя инструкцию '''use'''. Она записывается следующим образом:<br />
<br />
use ''адрес''<br />
<br />
Для неуправляемых модулей, адрес - это просто имя такого модуля.<br />
<br />
Для управляемых модулей, адрес - это префикс 'gbc:' плюс URL-адрес потока модуля. Например, "gbc:diss:/lib/module.gbc".<br />
<br />
Зависимости модулей - рекурсивные. Другими словами, если модуль B подключил модуль A, а модуль C подключил модуль B, то из модуля C будут доступны объявления модуля A.<br />
<br />
=== Объявление классов ===<br />
<br />
Для того, чтобы объявить класс, нужно написать следующую инструкцию:<br />
<br />
class ''имя_класса''<br />
<br />
Имя класса - это произвольная строка. Данная инструкция объявляет класс, в котором еще нет ни одного метода.<br />
<br />
Инструкция<br />
<br />
inherit ''имя_класса'', ''имя_родителя''<br />
<br />
добавляет класс ''имя_родителя'' в качестве родительского класса. Она должна быть записана после объявления класса. В этой инструкции, и класс ''имя_класса'', и класс ''имя_родителя'' могут быть объявлены во внешнем модуле.<br />
<br />
Существует также понятие родителя по-умолчанию. Они могут быть заданы только в неуправляемых модулях. Такие родители добавляются автоматически, если не указаны другие родительские классы.<br />
<br />
=== Объявление глобальных переменных ===<br />
<br />
Для того, чтобы объявить глобальную переменную - т.е. переменную, доступную из любой функции - нужно написать инструкцию<br />
<br />
global ''имя_переменной''<br />
<br />
В качестве имени переменной, опять же, может выступать любая строка.<br />
<br />
Глобальные переменные доступны только в пределах данного модуля. Для получения их значений из других модулей, следует использовать функции.<br />
<br />
По-умолчанию все глобальные переменные инициализируются нулем.<br />
<br />
=== Объявление функции ===<br />
<br />
Функция объявляется следующим образом:<br />
<br />
function ''имя_функции'', ''аргумент1'', ''аргумент2'', ..., ''аргументN''<br />
''тело функции''<br />
end<br />
<br />
Имя функции - это произвольная текстовая строка. Если имя имеет вид<br />
''имя_класса'':''имя_метода''<br />
, метод ''имя_метода'' добавляется в класс ''имя_класса'' (этот класс должен быть предварительно объявлен, но не обязательно в текущем модуле). В противном случае, объявляется функция, а не метод.<br />
<br />
Аргументы - это формальные параметры функции, они доступны в теле как локальные переменные.<br />
<br />
Список аргументов в какой-то мере условен. При вызове функции всегда можно указывать произвольное число параметров, это нигде не проверяется. Если фактических параметров меньше, чем формальных - оставшиеся аргументы будут инициализированы нулем. Если фактических больше - некоторые из них не будут доступны напрямую, однако стандартная библиотека может предоставлять функции, открывающие к ним доступ.<br />
<br />
Методы класса могут быть публичными (public), защищенными (protected) или частными (private). Публичные методы можно вызывать без ограничений, защищенные - только из данного класса и его потомков, частные - только из данного класса. Эти проверки делаются во время исполнения.<br />
<br />
По-умолчанию, метод является публичным. Для объявления защищенных и частных методов, нужно заменить ключевое слово function в объявлении метода на '''func_protected''' и '''func_private''' соответственно.<br />
<br />
Если метод/функция уже был объявлен ранее, он переобъявляется - т.е. данная реализация перекрывает предыдущую. Вызвать предыдущую реализацию можно при помощи оператора '''recall''' (см. ниже).<br />
<br />
Функция со специальным именем '''@@module_init''' является конструктором модуля; она вызывается в момент загрузки данного модуля. В ней, в частности, можно инициализировать глобальные переменные модуля.<br />
<br />
Метод '''@@init''' - конструктор класса. Он вызывается в момент создания объекта данного класса. Порядок вызова конструкторов таков: сначала рекурсивно вызываются конструкторы родительских классов в порядке объявления родителей, затем - конструктор данного класса. В конструкторе, в частности, можно инициализировать поля объекта.<br />
<br />
=== Специальные переменные ===<br />
<br />
Существует 6 зарезервированных имен переменных:<br />
* '''0''' (число ноль) - означает отсутствие объекта;<br />
* '''@true''' - указывает на специальный объект "истина". Этот объект не имеет полей и методов; он бывает полезен для написания логических конструкций;<br />
* '''@false''' - то же, что и 0;<br />
* '''@this''' - внутри функции-метода, указывает на текущий объект; внутри обычной функции равен 0;<br />
* '''@result''' - результат последней вызванной функции или оператора '''access''';<br />
* '''@exception''' - объект исключения, которое требуется обработать.<br />
<br />
=== Операторы функции ===<br />
<br />
'''Операторы''' - это управляющие инструкции, составляющие тело функции.<br />
<br />
Поскольку единственный существующий тип переменной - это указатель на объект, значительная часть операторов не требуется. Все операторы реализуются на уровне самих объектов в виде методов.<br />
<br />
Существуют следующие операторы:<br />
<br />
new ''переменная'', ''имя_класса'', ''строка''<br />
<br />
: создает новый объект класса ''имя_класса'' и записывает результат в указанную переменную. Если переменная была объявлена ранее - результат записывается в нее; если нет - создается новая '''локальная''' переменная (т.е. переменная, доступная только в пределах данной функции). Третий параметр - это некоторые данные для инициализации объекта. Они имеют значение только для некоторых классов в неуправляемых модулях. Кроме того, любой класс может быть инициализирован пустой строкой.<br />
<br />
bless ''переменная'', ''имя_класса''<br />
<br />
: эквивалент <tt>new</tt> без последнего параметра.<br />
<br />
mov ''переменная'', ''источник''<br />
<br />
: копирует в переменную ''переменная'' ссылку на существующий объект ''источник'' (аналогично, если переменная не была объявлена ранее - создается новая локальная переменная). Важно подчеркнуть, что при этой операции не создается нового объекта; две переменные будут указывать на один и тот же объект.<br />
<br />
goto ''имя_метки''<br />
<br />
: осуществляет безусловный переход к оператору, помеченному меткой ''имя_метки''.<br />
<br />
if ''переменная'', ''имя_метки''<br />
<br />
: осуществляет условный переход к оператору ''имя_метки'': если управляющая переменная (первый параметр) имеет отличное от нуля значение, осуществляется переход; в противном случае этот оператор игнорируется и выполнение продолжается со следующей инструкции.<br />
<br />
call ''переменная'', ''имя_функции'', ''аргумент1'', ..., ''аргументN''<br />
<br />
: если ''переменная'' - '''0''', вызывает функцию ''имя_функции''; в противном случае вызывает метод объекта, на который ссылается переменная. Как уже говорилось ранее, количество аргументов может быть любым вне зависимости от количества формальных параметров. Результат выполнения функции записывается в специальную переменную '''@result'''.<br />
<br />
: ''имя_функции'' может быть задано в форме ''имя_класса'':''имя_метода''. В этом случае осуществляется вызов метода конкретного класса, а не текущего (''имя_класса'' должно либо совпадать с классом объекта, либо быть его родительским классом). Это позволяет вызывать методы родительских классов, несмотря на перекрытие в дочерних классах.<br />
<br />
return ''переменная''<br />
<br />
: осуществляет выход из текущей функции с возвратом объекта, на который ссылается ''переменная''. Если выполнение функции доходит до конца, функция возвращает 0.<br />
<br />
access ''переменная'', ''имя_поля''<br />
<br />
: получает значение поля ''имя_поля'' объекта, на который ссылается ''переменная''. Значение записывается в специальную переменную '''@result'''. Если указанного поля в классе нет, в '''@result''' записывается 0.<br />
<br />
mutate ''переменная'', ''имя_поля'', ''значение''<br />
<br />
: записывает объект, на который ссылается ''значение'', в поле ''имя_поля'' объекта, на который ссылается ''переменная''. Если такого поля ранее не существовало, оно создается.<br />
<br />
recall<br />
<br />
: вызывает замещенную функцию с теми же параметрами, которые были переданы данной функции; осуществляет выход из текущей функции с результатом, который вернула замещенная функция.<br />
<br />
upgrade ''переменная'', ''имя_класса''<br />
<br />
: расширяет объект, на который ссылается ''переменная'', до класса ''имя_класса''. Это делается путем вызова конструкторов классов, которые в дереве иерархии находятся между классом переменной и указанным классом. Очевидно, класс объекта должен быть родительским для класса ''имя_класса''. После выполнения данной инструкции, класс объекта станет равным ''имя_класса'', т.е. для него будут доступны все методы этого класса.<br />
<br />
use_locals ''имя_функции''<br />
<br />
: открывает данной функции доступ к локальным переменным функции ''имя_функции''. Этот оператор может быть указан только в самом начале тела функции и только единожды. ''имя_функции'' записывается как при объявлении: для обычной функции - просто ее имя, для метода класса - имя класса и имя метода, разделенные двоеточием. Для того, чтобы локальные переменные реально стали доступны, функция ''имя_функции'' должна вызвать текущую функцию специальным образом.<br />
<br />
: '''ПРИМЕЧАНИЕ''': такой специальный вызов на данный момент невозможно сделать напрямую. Класс [[Closure]] стандартной библиотеки позволяет создать указатель на метод, при вызове которого (через этот указатель) происходит требуемый тип вызова. В будущем планируется добавить оператор вызова '''ccall''', выполняющий эту операцию.<br />
<br />
: данная инструкция требуется для реализации [http://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%BC%D1%8B%D0%BA%D0%B0%D0%BD%D0%B8%D0%B5_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29 замыканий].<br />
<br />
Кроме перечисленных, существуют еще операторы для работы с исключениями; они рассмотрены в следующем подразделе.<br />
<br />
=== Обработка исключений ===<br />
<br />
'''Исключения''' - это специальный механизм, предназначенный для передачи информации об ошибках. Он заключается в следующем.<br />
<br />
Любая функция может бросить исключение. Само исключение - это некоторый объект произвольного класса. Когда такое происходит, виртуальная машина начинает искать ближайший обработчик исключений. Если сама функция, бросившая исключение, может его обработать - исполнение передается соответствующей инструкции. В противном случае, начинается раскрутка стека вызовов до первой функции, которая может обработать исключение. Если ни одна функция этого сделать не может, выполнение программы прекращается, а вызвавшему модулю сообщается о необработанном исключении.<br />
<br />
В функции может быть установлено несколько обработчиков исключений, которые формируют стек. Обработчик - это некоторая метка в пределах функции. Когда функции требуется обработать исключение, управление передается оператору, на который ссылается эта метка, а сама эта метка извлекается из стека обработчиков. Другими словами, в функции может быть установлено несколько обработчиков, которые, при необходимости, будут вызываться в порядке, обратном их установке. Если же возникает исключение, а в функции не установлено ни одного обработчика - происходит выход из функции, а исключение передается функции, которая вызвала данную функцию, и т.д.<br />
<br />
Для работы с исключением существуют следующие четыре оператора:<br />
<br />
throw ''переменная''<br />
<br />
: кидает исключение, передав в качестве параметра объект, на который ссылается ''переменная''.<br />
<br />
except_push ''имя_метки''<br />
<br />
: добавляет метку ''имя_метки'' в стек обработчиков исключений для данной функции. Когда возникает исключение, управление передается указанной метке, а объект исключения записывается в специальную переменную '''@exception'''.<br />
<br />
except_pop<br />
<br />
: изымает из стека обработчиков метку, находящуюся на вершине стека.<br />
<br />
except_clr<br />
<br />
: очищает стек обработчиков исключений для данной функции.<br />
<br />
Кроме оператора '''throw''', исключения могут кидаться неуправляемыми модулями. Кроме того, существует механизм, при помощи которого ошибки, возникающие на уровне самой виртуальной машины, передаются в стандартную библиотеку, которая кидает исключение.<br />
<br />
== Формат байт-кода gide ==<br />
<br />
<font color="red">'''ПРИМЕЧАНИЕ:''' в данном разделе приведена устаревшая информация, которая не соответствует действительности. Раздел оставлен для ознакомления с общей концепцией хранения байт-кода. Через некоторое время он будет переписан.</font><br />
<br />
Байт-код gide представляет собой популярный BER-формат: весь файл разбит на секции, каждая из которых начинается с описания ее типа и размера. <br />
<br />
Следует сразу оговориться, что все числа в gide представляются в формате big endian (т.е. наименее значимый байт впереди). <br />
<br />
Заголовок BER-секции представляет собой последовательность кода секции (1 байт) и ее размер в BER-формате.<br />
<br />
Все целые числа в байт-коде, если это не оговорено специально, представляются в BER-формате: для записи числа требуется столько байт, сколько нужно; в каждом байте записывается 7 битов числа. Если самый старший бит равен 1, требуется прочитать/записать следующий байт. Если старший бит равен 0 - это последний байт, описывающий число.<br />
<br />
=== Секция имен ===<br />
<br />
Тип секции - код символа N.<br />
<br />
Для большей компактности кода используется секция имен. В ней последовательно записываются все текстовые строки (имена переменных/функций, данные конструкторов и пр.), которые встречаются в скрипте. Одинаковые текстовые строки записываются только один раз. Таким образом, каждая текстовая строка получает свой уникальный номер, и в байт-коде вместо строк используются эти номера.<br />
<br />
Поскольку в скрипте часто встречаются одинаковые строки (например, работа с одной и той же переменной), это сильно уменьшает размер кода.<br />
<br />
Все строки записываются следующим образом:<br />
* длина строки (в BER формате, см. выше);<br />
* текст.<br />
<br />
Нумерация строк начинается не с нуля, а с 16. Меньшие номера зарезервированы для стандартных имен:<br />
* Переменная @null (она же 0) имеет номер 0;<br />
* Переменная @this имеет номер 1;<br />
* Переменная @false имеет номер 2;<br />
* Переменная @true имеет номер 3.<br />
<br />
В случае обращения к полю объекта, в байт-код записывается все составное имя объекта (вместе с разделяющими точками). Виртуальная машина сама разбирает составные имена.<br />
<br />
=== Секция экспорта ===<br />
<br />
Тип секции - ASCII-код символа E<br />
<br />
Данная секция предназначена для упрощения навигации по секции кода.<br />
<br />
Для каждой функции, описанной в модуле, записывается:<br />
* индекс имени функции;<br />
* смещение функции относительно начала секции кода.<br />
<br />
=== Секция кода ===<br />
<br />
Тип секции кода - ASCII-код символа C.<br />
<br />
В эту секцию записываются тела функций Gide.<br />
<br />
Для каждой функции записывается:<br />
* индекс имени функции (он должен быть смещен от начала секции кода на количество байт, указанных в секции экспорта);<br />
* количество аргументов функции;<br />
* индексы имен аргументов функции;<br />
* размер блока операторов;<br />
* для каждого оператора:<br />
** индекс имени оператора;<br />
** количество передаваемых параметров функции (только для оператора call);<br />
** индексы имен всех параметров.<br />
<br />
Индексы операторов:<br />
* Простые операторы имеют номера от 1 до 9 в порядке их перечисления в п.2.1;<br />
* Оператор создания объекта имеет номер 10;<br />
* Оператор throw имеет номер 13;<br />
* Оператор except_push имеет номер 16;<br />
* Оператор except_pop имеет номер 17;<br />
* Оператор создания переменной пользовательского класса имеет номер 11;<br />
* Оператор расширения наследования имеет номер 15;<br />
* Оператор use имеет номер 12;<br />
* Оператор external номер 14;<br />
<br />
=== Секция импорта ===<br />
<br />
Тип секции импорта - ASCII-код символа I<br />
<br />
В эту секцию записываются подключаемые операторами use и external библиотеки.<br />
<br />
Для каждой библиотеки записывается:<br />
* число 1 в случае оператора external, 0 - в случае use;<br />
* индекс пути к библиотеке.<br />
<br />
=== Секция наследования классов ===<br />
<br />
Тип секции - код символа H.<br />
<br />
В эту секцию записывается статическая таблица наследований классов.<br />
<br />
Для каждого оператора inherit записывается:<br />
* индекс наследующего класса;<br />
* индекс наследуемого класса.<br />
<br />
=== Секция номеров строк ===<br />
<br />
Тип секции - код символа D.<br />
<br />
Эта секция не является обязательной. В ней записываются соответствия операторов и номеров строк в исходных файлах, на которых указан соответствующий оператор.<br />
<br />
В секцию записывается массив следующих структур:<br />
* индекс имени файла;<br />
* размер блока функций;<br />
* для каждой функции:<br />
** индекс имени функции;<br />
** размер блока операторов;<br />
** для каждого оператора:<br />
*** номер оператора в теле функции;<br />
*** номер строки кода, на которой объявлен оператор.</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE%D0%B4%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B_%D0%B2%D0%B2%D0%BE%D0%B4%D0%B0Описание подсистемы ввода2013-07-13T11:45:10Z<p>Raw mat: </p>
<hr />
<div>Подсистема ввода отвечает за считывание вводимой пользователем информации со всевозможных устройств (клавиатура, мышь, джойстик и пр.) и формирование на их основе событий ввода, которые затем передаются различным модулям. Подсистема предоставляет полную абстракцию от устройств ввода: в модули-обработчики передаются уже готовые события, связанные с конкретными действиями.<br />
<br />
== Схема обработки ввода пользователя ==<br />
<br />
Обработка пользовательского ввода состоит из следующих этапов.<br />
<br />
1. Устройство ввода (клавиатура, мышь и пр.) фиксирует действие пользователя (нажатие кнопки, движение мышью и т.д.) и передает сообщение об этом в движок ввода. В таком сообщении содержится тип события и всевозможные параметры. Например, когда пользователь нажимает кнопку на клавиатуре - передается событие key_press с двумя параметрами: идентификатор нажатой кнопки и unicode-символ, который ей соответствует. Когда пользователь отпускает кнопку - передается событие key_release с теми же параметрами.<br />
<br />
2. Движок ввода передает это событие всем активным '''контекстам ввода'''.<br />
<br />
Контекст ввода - это таблица преобразования низкоуровневых событий в пользовательские. Контекст ввода определяет правила, по которым события будут передаваться пользователю. Таких контекстов может быть несколько: например, для управления аватаром используется один контекст, а для работы с GUI - другой.<br />
<br />
Каждый из контекстов ввода обрабатывает низкоуровневые события независимо. Делает он это при помощи таблицы обработки событий. В этой таблице каждому низкоуровневому событию, удовлетворяющему определенным критериям (или же последовательности таких событий), соответствует вызов одного или нескольких обработчиков ввода.<br />
<br />
В качестве критерия могут использоваться различные условия на параметры события. Например, в качестве условия может выступать:<br />
* нажатие любой клавиши (никакого критерия нет);<br />
* нажатие любой клавиши при нажатой клавише shift (критерий модификатора);<br />
* нажатие любой клавиши при отпущенной клавише ctrl (критерий модификатора);<br />
* нажатие клавиши A (критерий параметра события);<br />
* нажатие клавиши A при нажатой левой клавиши мыши (смешанный критерий).<br />
* и т.д.<br />
<br />
3. Контекст ввода передает результат обработки события обработчикам ввода. Обработчик - это небольшой модуль, который выполняет с полученным событием какие-либо действия и отправляет результат пользователю.<br />
<br />
== Конфигурация ввода ==<br />
<br />
Файл конфигурации движка ввода находится по адресу '''/etc/world/input.conf''' в DISS.<br />
<br />
Формат этого файла следующий.<br />
<br />
Пустые строки и строки, начинающиеся с #, игнорируются.<br />
<br />
В начале файла перечислены устройства ввода, которые необходимо подключить. Устройство подключается директивой '''device''', за которой следует URL устройства - его тип и, возможно, какие-то параметры после двоеточия. У стандартных устройств никаких параметров нет. Так, клавиатура и мышь подключается директивами<br />
<br />
device keyboard<br />
device mouse<br />
<br />
Далее идут описания контекстов.<br />
<br />
Описание контекста начинается с директивы '''context''', параметр которой - имя контекста. После этой директивы идет заполнение таблицы ввода директивой '''on'''.<br />
<br />
Рассмотрим пример привязки нажатия клавиши A к отправке события объекту:<br />
<br />
device keyboard<br />
device mouse<br />
<br />
context local_control<br />
<br />
on key_press check(key=A) do send_event:power_on<br />
<br />
Контекст '''local_control''' - используется для управления объектом на локальной сцене, т.е. для тестирования.<br />
<br />
Директива on создает в этом контексте обработчик события '''key_press''' (нажатие на клавишу клавиатуры) с критерием нажатия клавиши A. После ключевого слова '''do''' следуют URL-ы обработчиков этого события - в данном случае, это обработчик '''send_event''', который отправит событие '''power_on''' объекту, которым управляет пользователь.</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%86%D0%B8%D0%B8Основные синтаксические конструкции2013-07-13T11:43:30Z<p>Raw mat: </p>
<hr />
<div>В этой главе будут рассмотрены основные синтаксические конструкции, которые так или иначе присутствуют в любом современном языке программирования, будь то функциональный или объектно-ориентированный язык. Это констркции условного перехода и циклы. Без подобных конструкций (или их аналогов) невозможно написать программу, сколько-нибудь сложнее чем "Hello world". <br />
<br />
__TOC__<br />
<br />
== Условный оператор ==<br />
<br />
Условный оператор — это основа основ любого языка программирования. На основании условия, то есть значения некоторого выражения, принимается решение, куда следует двигаться программе. В основе всех условных операторов лежит булева логика, при которой любое утверждение сводится к двум понятиям: истина и ложь. Если условие истинно, то управление программы движется в одну сторону. Если условие ложно — в другую. Таким образом, в каждый момент времени делается выбор всего из двух направлений. Более сложные условия строятся путем последовательного прохождения элементарных, двоичных условий.<br />
<br />
=== Оператор if ===<br />
<br />
В языке K++, для проверки условия служит оператор <tt>'''if'''</tt>, так же называемый условным оператором. Синтаксис этого оператора знаком наверное любому человеку, который имеет хоть какое-то представление о языках программирования:<br />
<source lang="kpp"><br />
if (/* выражение условия */) {<br />
/* тело оператора */<br />
}<br />
</source><br />
<br />
Если в коде программы встречается условный оператор, то первым делом происходит вычисление значения его условия — выражения, заключенного в круглые скобки. В качестве такого выражения, может быть указано любое выражение, которое возвращает значение. Если значение условного выражения можно трактовать как истину, то управление передается телу оператора, заключенному в фигурные скобки (в случае единственного выражения в теле оператора, фигурные скобки можно опустить). Если же значение условия ложно — управление передается следующему выражению, стоящему после условного оператора (то есть, тело оператора просто пропускается).<br />
<br />
Истинным, считается любой существующий объект, а так же логические [[Типы операторов#Операторы сравнения|операторы сравнения и отношения]] (такие как <tt>==</tt>, <tt>!=</tt>, <tt><</tt> и т. д), если их значение истинно (то есть, объекты соответствуют указанному отношению), а так же специальное ключевое слово <tt>'''true'''</tt>, которое считается истинным всегда. Исключением является объект типа bool, имеющий в данный момент значение <tt>'''false'''</tt>.<br />
<br />
Ложными, соответственно, считаются несозданные объекты (пустые указатели), а так же операторы отношения, если их значение ложно (объекты не соответствуют отношению). Ложными так же считаются объекты, соответствующие ключевым словам <tt>'''false'''</tt> и <tt>'''null'''</tt>. Опять же, исключением является объект типа bool, если он имеет значение <tt>'''true'''</tt>.<br />
<br />
Для ясности приведем несколько выражений, а так же укажем соответствующие им логические значения:<br />
<source lang="kpp"><br />
//Выражение булево значение<br />
true true<br />
false false<br />
null false<br />
var x; x false<br />
var y; y = 1; true<br />
var int z; z true<br />
var bool x; false<br />
var bool x; x = true; true<br />
0 true<br />
1 true<br />
"hello world" true<br />
0 == 1 false<br />
5 < 6 true<br />
2 > 3 false<br />
5 >= 5 true<br />
'a' != 'b' true<br />
</source><br />
<br />
Обратите внимание на следующие моменты:<br />
* Неинициализированная динамическая переменная ''x'' считается ложной (равна <tt>'''null'''</tt>)<br />
* Инициализированная динамическая переменная ''y'' считается истинной (объект создан)<br />
* Неинициализированная статическая переменная ''z'' считается истинной (объект создан)<br />
* Переменная типа <tt>bool</tt> имеет значение по умолчанию <tt>'''false'''</tt> (хотя объект создан)<br />
* 0, так же как любой другой созданный объект считается '''истиной''', в отличие от C++. <br />
* условие 5 >= 5 считается истиной, так как формулируется как "5 больше 5 '''или''' равно 5"<br />
<br />
По поводу нуля следует поговорить отдельно. C++ не является в полной мере объектно-ориентированным языком, фактически, в нем ОО система является абстракцией, позволяющей оперировать все теми же понятиями стандартных типов данных и памяти, но на более высоком уровне. Все что мы имеем на этапе выполнения программы, это кусочки памяти, которые мы считаем объектами. Поскольку числа в нем являются совершенно определенной сущностью, подобные вольности возможны.<br />
<br />
В языке К++ все наоборот. Все с чем он оперирует — это объекты. Провести грань между стандартными типами данных и пользовательскими невозможно. Числа, что фигурируют в программе так же являются объектами. Даже числовые константы которые записываются в выражениях — это тоже объекты (это уже было показано в [[Введение, или краткий обзор#Расширение классов|кратком обзоре]]). За основу логики языка принимается факт существования объекта. Если объекта не существует — значение ложно, если существует — истинно (кроме описанных выше исключений). Оператор <tt>'''if'''</tt> помимо чистой логики, служит для определения существования объекта. Получается, что при таком подходе, делать для нуля исключение нерационально, и более того — это может привести к ошибкам. Возьмем к примеру код:<br />
<br />
<source lang="kpp"><br />
if (x) {<br />
/* код, соответствущий существованию переменной x */<br />
}<br />
</source><br />
<br />
Подразумевается, что тело оператора будет выполнено в случае, если передаваемый объект (вероятнее всего [[Переменные#Нетипированные (динамические) переменные|динамическая переменная]]) инициализирован и существует. Но что будет, если мы передадим в качестве объекта динамическую переменную, содержащую объект типа <tt>int</tt>, равный нулю? Если бы реализация языка обрабатывала значение 0 как ложь, то логика оператора <tt>'''if'''</tt> в частости и всей программы в целом, зависела бы от фактического значения переменной, а соответственно была бы неопределенной и даже непредсказуемой. Получалось бы, что для одних переменных, оператор работает одним образом, а для других — другим. Отладка подобной программы может занять многие часы. А все из-за одной условности.<br />
<br />
Таким образом, можно заключить, что оператор <tt>'''if'''</tt> следует применять для проверки логического выражения, либо для проверки существования переменной. Если мы хотим проверить равенство переменной нулю, то это необходимо делать явно:<br />
<source lang="kpp"><br />
if (x == 0) {<br />
/* что нужно делать, если x равен 0 */<br />
}<br />
</source><br />
<br />
=== Оператор if-else ===<br />
<br />
Если нам необходимо организовать ''ветвление'', то есть в случае истинности, выполнить один участок кода, а в случае ложности другой, то применяется расширенная форма оператора <tt>'''if-else'''</tt>:<br />
<source lang="kpp"><br />
if (/* выражение условия */) {<br />
/* код, соответствущий истине */<br />
} else {<br />
/* код, соответствущий лжи */<br />
}<br />
</source><br />
<br />
Если условие истинно, то будет выполнен первый блок, если ложно то второй. <br />
<br />
<br />
Условные операторы, подобно многим другим, позволяют организовывать вложения, когда в тело одного условного оператора вкладывается один или несколько других условных операторов, например:<br />
<source lang="kpp"><br />
if (/* условие 1 */) {<br />
/* если условие 1 истинно: */<br />
if (/* условие 2 */) {<br />
/* если условие 2 истинно */<br />
}<br />
if (/* условие 3 */) {<br />
/* если условие 3 истинно */<br />
}<br />
} else {<br />
/* если условие 1 ложно: */<br />
if (/* условие 4 */) {<br />
/* если условие 4 истинно */<br />
} else {<br />
/* код, соответствущий лжи условия 4 */<br />
}<br />
}<br />
</source><br />
Таким образом можно строить разветвленные деревья условий, которые и реализуют алгоритмы программ.<br />
<br />
=== Оператор if-elsif-else ===<br />
<br />
Существует так же третья форма условного оператора, которая позволяет проверять несколько условий в единой, более эффективной форме. Это конструкция <tt>'''if-elsif-else'''</tt>:<br />
<source lang="kpp"><br />
if (/* условие 1 */) {<br />
/* код, соответствущий истине условия 1*/<br />
} elsif (/*условие 2*/) {<br />
/* код, соответствущий истине условия 2*/<br />
...<br />
} else {<br />
/* код, соответствущий лжи */<br />
}<br />
</source><br />
Она работает так: сначала проверяется условие 1. Если оно истинно, то выполняется код в первом блоке, а затем управление выходит за рамки всей связки. Если условие 1 ложно, то проверяется условие 2, и т. д. Таким образом, вышеописанная конструкция функционально эквивалентна следующей конструкции, однако более компактна.<br />
<br />
<source lang="kpp"><br />
if (/* условие 1 */) {<br />
/* если условие 1 истинно */<br />
} else {<br />
if (/* условие 2 */) {<br />
/* если условие 2 истинно */<br />
} else {<br />
if (/* условие 3 */) {<br />
/* если условие 3 истинно */<br />
} else {<br />
...<br />
}<br />
}<br />
}<br />
</source><br />
<br />
=== Постфиксные операторы ===<br />
<br />
Существует так же более лаконичная форма оператора <tt>'''if'''</tt>, удобная для записи коротких, однострочных условий. При этом, само условие записывается после выражения, которое оно должно проверить:<br />
<br />
<source lang="kpp"><br />
/* выражение */ if /* условие */;<br />
</source><br />
<br />
При такой записи, окружать условие скобками не требуется. Проверяемое выражение должно быть единственным, то есть не содержать оператора точка с запятой (<tt>;</tt>). Такие условия занимают меньше места в программе и помогут сделать ваш код более чистым:<br />
<br />
<source lang="kpp"><br />
var even_sum = 0;<br />
for (var i = 1; i < 100; i++)<br />
even_sum += i if i % 2 == 0; //сумма всех четных чисел<br />
</source><br />
<br />
Для проверки "невыполнения" условия есть специальная форма постфиксного оператора — <tt>'''unless'''</tt>. Выражение при таком операторе будет выполняться только если условие ложно. Таким образом, смысл оператора прямо противоположен оператору <tt>'''if'''</tt>. Конечно, эти два оператора взаимозаменяемы. Конструкция <tt>do_smth '''if''' x;</tt> делает то же самое что <tt>do_smth '''unless''' !x;</tt>. <br />
<br />
Перепишем предыдущий код с использованием оператора <tt>'''unless'''</tt>:<br />
<br />
<source lang="kpp"><br />
var even_sum = 0;<br />
for (var i = 1; i < 100; i++)<br />
even_sum += i unless i % 2 != 0; //сумма всех четных чисел<br />
</source><br />
<br />
=== Тернарный условный оператор ===<br />
<br />
В некоторых случаях, может возникнуть ситуация, при которой необходимо присвоить значение некоторой переменной на основании условия. То есть, при истинности условия присвоить одно значение, а при ложности — другое. Это можно осуществить двумя способами. Первый способ — использовать обычный условный оператор:<br />
<source lang="kpp"><br />
var x = 5;<br />
var y = 0;<br />
if (x > 3)<br />
y = 3;<br />
else <br />
y = -1;<br />
</source><br />
<br />
Не будем уточнять, зачем кому-то мог понадобиться подобный "гениальный" код, а отметим лишь то, что операция присвоения занимает 4 строки при соблюдении правил форматирования кода. В тех случаях, когда тела условного оператора представляют собой простые выражения "в одну строчку", особенно если, как уже было сказано выше, происходит присвоение, разумно применять специальную форму условного оператора — тернарный оператор <tt>?:</tt>. Тогда, тот же код будет выглядеть так:<br />
<source lang="kpp"><br />
var x = 5;<br />
var y = 0;<br />
y = x > 5 ? 3 : -1;<br />
</source><br />
<br />
Это единственный в языке К++ тернарный оператор, работающий с тремя выражениями сразу. Первое выражение — это условие; оно располагается слева, до знака вопроса. Между знаком вопроса и знаком двоеточия расположено выражение, которое должно подставляться, в случае истинности условия. Последним располагается выражениие, подставляющееся в случае ложности.<br />
<br />
Данная форма оператора очень удобна для записи непосредственно внутри выражений, фактических параметров в вызовах функций, либо любых других выражениях. Его даже можно применять в операторе подстановки <tt>#{ }</tt> в строках. Можно его применять и без возврата значения, например так:<br />
<source lang="kpp"><br />
while (true) {<br />
CheckState() != State.OK ? Signal() : Sleep();<br />
}<br />
</source><br />
<br />
'''Примечание:''' Работать такой код будет если и <tt>Signal()</tt> и <tt>Sleep()</tt> возвращают значения.<br />
<br />
== Оператор множественного выбора (switch) ==<br />
<br />
В тех случаях, когда требуется сравнить значение некоторого выражения с большим количеством возможных значений, применять обычные условные операторы неразумно. Код получится громоздким и трудно читаемым, а самое главное — изменять такой код будет очень сложно. В таких случаях, на помощь программисту приходит оператор множественного выбора, или оператор <tt>'''switch'''</tt>. <br />
<br />
Предположим, что перед нами стоит такая задача: функция получает некоторый числовой код ошибки и на основании него должна выполнять различные действия. В частности, выводить соответствующие сообщения. Если от нас требуется только вернуть соответствующую коду ошибки строку, проще всего и эффективнее, разумеется, применять [[Стандартные типы данных#Хеши|хеши]], но мы будем считать что требуется сделать что-то еще. <br />
<br />
Конструкция множественного выбора реализуется следующим образом: указывается ключевое слово <tt>'''switch'''</tt>, после которого, в круглых скобках указывается выражение-''селектор'', которое мы будем анализировать. Затем, ставятся фигурные скобки switch-блока, в теле которого могут быть только конструкции, относящиеся непосредственно к оператору. В нашем случае это будет примерно так:<br />
<source lang="kpp"><br />
function ProcessError(const int errno) {<br />
switch (errno) {<br />
/* switch блок */ <br />
}<br />
}<br />
</source><br />
<br />
Далее, мы должны определить относящиеся к делу случаи, а именно, перечислить конкретные значения, с которыми мы будем сравнивать код ошибки и на основании этого сравнения будем выполнять некоторые операции. Это осуществляется с помощью подоператора <tt>'''case'''</tt>, который записывается так:<br />
<source lang="kpp"><br />
case /* выражение сравнения */: /* обработчик */<br />
</source><br />
<br />
Выражение сравнения — это любое выражение, которое будет сравниваться с селектором. В качестве такого выражения может быть выбрано не одно, а сразу несколько условий. Тогда они должны отделяться друг от друга запятой: <br />
<source lang="kpp"><br />
case /*условие1*/, /*условие2*/, ... /*условие N*/: /* обработчик */<br />
</source><br />
<br />
В качестве условия может так же быть указан [[Стандартные типы данных#Интервалы|интервал]]. Тогда будет проверяться, попадает ли значение выражения селектора в заданный интервал. Например, если нам необходимо анализировать численное выражение, то один из операторов <tt>'''case'''</tt> может выглядеть так:<br />
<source lang="kpp"><br />
case 1, 2, 5 .. 10, 12: /* обработчик */<br />
</source><br />
<br />
Для срабатывания обработчика достаточно, чтобы сработало хотябы одно из условий, перечисленных в списке; тогда управление будет передано коду обработчика. Обработчиком может быть либо отдельное выражение, либо блок. Во втором случае, его необходимо заключить в фигурные скобки. В отличие от C++, после обработчика управление передается за пределы оператора <tt>'''switch'''</tt>, а не на следующее сравнение. То есть, логика работы более близка к паскалевской конструкции <tt>'''case'''</tt>.<br />
<br />
Для безусловного выхода из любого места обработчика, может применяться ключевое слово <tt>'''break'''</tt>. При этом, управление будет передано коду, следующему за оператором <tt>'''switch'''</tt>.<br />
<br />
Если требуется рассмотреть отдельный случай, когда ни одно из условий не было удовлетворено (то есть, ни один из операторов <tt>'''case'''</tt> не сработал), применяется ''действие по умолчанию''. Для этого, необходимо записать ключевое слово <tt>'''default'''</tt>, а затем через двоеточие указать обработчик. Естественно, никакого выражения сравнения тут нет. <br />
<br />
'''Примечание:''' при использовании обработчиков по умолчанию, следует иметь в виду что: <br />
* на кажый оператор <tt>'''switch'''</tt> может быть указан только один обработчик по умолчанию<br />
* обработчик по умолчанию должен быть записан в самом конце конструкции<br />
<br />
<br />
Обобщив вышесказанное, напишем наконец, реализацию задуманной нами функции:<br />
<source lang="kpp" line="1"><br />
function ProcessError(const int errno) {<br />
switch (errno) {<br />
case AGAIN: {<br />
Log("требуется повтор операции"); <br />
/* запрашиваем у пользователя подтверждение на повтор */ <br />
}<br />
case ACCESS_DENIED: <br />
Log("ошибка доступа"); <br />
case REQUEST_TIMEOUT, TIMEOUT: <br />
Log("таймаут"); <br />
default:<br />
Log("неизвестный код ошибки: #{errno}");<br />
}<br />
}<br />
</source><br />
<br />
<br />
;3-6: Поскольку выражение обработчик сложное, то есть состоит более чем из одной конструкции, мы заключили его в фирурные скобки.<br />
<br />
;9: Для случаев REQUEST_TIMEOUT и TIMEOUT был использован один обработчик. Подразумевается, что коды, соответствующие данным ошибкам разные, но программист решил их объединить в одно целое.<br />
<br />
== Циклы ==<br />
<br />
Циклы представляют собой еще одну неотъемлемую часть любого языка программирования. Сущность циклов заключается в том, что некоторый участок кода (тело цикла), на основании некоторого условия выполняется определенное количество раз (итераций). Различают циклы с предпроверкой условия и с постпроверкой. В первом случае, сначала проверяется условие, а затем выполняется переход. Во втором случае, тело цикла выполняется как минимум один раз, после чего проверяется условие и принимается решение о повторении, либо о выходе.<br />
<br />
=== Цикл while ===<br />
<br />
Простейшим из циклов является цикл <tt>'''while'''</tt>, который выглядит следующим образом:<br />
<source lang="kpp"><br />
while (/*условие*/) {<br />
/* тело цикла */<br />
}<br />
</source><br />
<br />
[[Изображение:While_loop.png|thumb|left|Блок схема цикла while]] <br />
<br />
Цикл <tt>'''while'''</tt> является циклом с предпроверкой условия, то есть, сначала проверяется значение выражения в условии цикла, а затем выполняется переход на тело цикла (если условие истинно), либо за пределы цикла (если ложно). Схематически, поведение цикла изображено на блочной диаграмме слева. <br />
<br />
Условием цикла может быть любое выражение, которое возвращает значение. При этом оно трактуется как логическое выражение, что определяет то же самое поведение, что и у условных операторов. Тело цикла может представлять собой одну или несколько конструкций. В случае нескольких конструкций, тело цикла следует заключить в фигурные скобки.<br />
<br />
Тело цикла представляет собой контекст. Таким образом, переменные, объявленные в теле цикла, не будут видны за его пределами. Разумеется, переменные объявленные выше по иерархии, будут видны в теле цикла. В целом, с этой точки зрения все происходит точно так же как и в теле самих функций.<br />
<br />
В теле цикла могут использоваться любые конструкции, что и в теле самих функций. Допускаются вложенные циклы.<br />
<br />
<br clear="both" /><br />
<br />
'''Примечание:''' Существует так же постфиксная форма оператора <tt>'''while'''</tt>, принципы использования которой схожи с таковыми в случае [[#Постфиксные операторы|постфиксных условных операторов]]. Например так можно посчитать сумму чисел от одного до ста практически "в одно действие":<br />
<br />
<source lang="kpp"><br />
var i = 0, s = 0;<br />
s += i++ while i < 100;<br />
</source><br />
<br />
=== Цикл for ===<br />
<br />
[[Изображение:For_loop.png|thumb|left|Блок схема цикла for]] <br />
Более сложным примером является цикл <tt>'''for'''</tt>. Как правило, он применяется в случаях, когда есть некоторая переменная изменяющая свое значение в определенных пределах и сохраняющая свое значение в течение всей итерации. В конструкции цикла <tt>'''for'''</tt> собраны все необходимые операции, что позволяет оперировать переменной более удобным образом. Опять же, исключается возможность ошибки, при которой программист забыл выполнить одну из операций. <br />
<br />
Цикл <tt>'''for'''</tt> работает следующим образом. Сначала выполняется код инициализации переменной цикла (ее еще называют управляющей переменной). Как видно из блочной диаграммы, это происходит только один раз, в самом начале. Затем, проверяется условие. Если условие истинно, управление передается телу цикла. Подразумевается, что в нем будут производиться некоторые действия, учиывающие текущее значение переменной цикла. После выполнения тела цикла, производится приращение управляющей переменной. Далее управление опять передается коду проверки условия; так повторяется до тех пор пока значение выражения условия не станет ложным. <br />
<br />
<br clear="both" /><br />
В отличие от паскаля, цикл <tt>'''for'''</tt> совершенно не обязательно должен работать с числами. В роли управляющей переменной может выступать любой объект. Синтаксис оператора <tt>'''for'''</tt> выглядит так:<br />
<source lang="kpp"><br />
for (/* инициализатор */; /* условие */ ; /* приращение */) {<br />
/* тело цикла */<br />
}<br />
</source><br />
<br />
Выражение-инициализатор представляет собой выражение, объявляющее управляющую переменную цикла, либо просто инициализирующее некоторую переменную начальным значением. Объявляемая управляющая переменная будет иметь область видимости, соответствующую телу цикла, и не будет видна за его пределами. Внешняя переменная, естественно, будет видна в пределах той области, в которой она была объявлена. В этом случае, цикл просто использует ее, изменяя значения. При выходе из цикла, внешняя переменная будет иметь значение, соответствующее последней итерации, плюс одно приращение (см. диаграмму).<br />
<br />
Если требуется объявление, то следует использовать конструкцию объявления переменной, точно такую же как и в обычной практике. То есть, правила объявления переменных и инициализации те же. Выражение-условие содержит некоторое выражение (обычно оператор отношения), которое должно менять свое значение в зависимости от значения управляющей переменной. Наконец, выражение приращения используется для изменения значения управляющей переменной на некоторую атомарную величину. В случае целочисленного типа управляющей переменной, для этого, как правило, используется оператор <tt>++</tt> (поскольку выражение приращения выполняется отдельно от тела, здесь не имеет значения, какую форму оператора использовать).<br />
<br />
В зависимости от ситуации, некоторые из трех вышеперечисленных выражений могут быть опущены. Следующие циклы, по сути, являются функционально эквивалентными:<br />
<br />
{| width=65% align=center<br />
| <source lang="kpp"><br />
for (var i = 0; i < 10; i++) {<br />
/* тело */ <br />
<br />
}<br />
</source><br />
| <source lang="kpp"><br />
var i = 0;<br />
for (; i < 10; i++) {<br />
/* тело */ <br />
}<br />
</source><br />
|-<br />
| <source lang="kpp"><br />
for (var i = 0; ; i++) {<br />
if (i >= 10)<br />
break; <br />
/* тело */ <br />
}<br />
</source><br />
| <source lang="kpp"><br />
for (var i = 0; i < 10; ) {<br />
/* тело */ <br />
i++;<br />
<br />
}<br />
</source><br />
|}<br />
<br />
Подобные изменения могут быть оправданными в некоторых задачах, где требуется более тонкое управление поведением цикла. Чаще всего применяются приемы использования внешней переменной и вынесение приращения в тело цикла.<br />
<br />
Ключевое слово <tt>'''break'''</tt> служит для безусловного выхода за пределы цикла, независимо от основного условия и количества "оставшихся" итераций. Оно будет рассмотрено ниже.<br />
<br />
'''Примечание:''' Несмотря на то, что синтаксис цикла и логика его работы очень похожи на те, что присутствуют в языке C++, существуют и отличия. В частности, при использовании вложенных циклов, допускается (хоть и не рекомендуется) использование одного и того же имени управляющей переменной. Логика их использования соответствует логике [[Функции#Область видимости|применения локальных переменных в функциях]]:<br />
<source lang="kpp" line=1><br />
for (var i = 1; i < 10; i++) {<br />
puts("(1) i = #{i}");<br />
for (var i = 1; i < 5; i++)<br />
puts("(2) i = #{i}");<br />
puts("(3) i = #{i}");<br />
}<br />
</source><br />
<br />
<br />
<br />
Приведем несколько более практичных примеров использования цикла в различных задачах:<br />
<source lang="kpp" line=1><br />
var s = 0, var i = 1; <br />
var ary = [];<br />
for ( ; i < 10; i++) {<br />
//используется внешняя переменная<br />
s += i;<br />
puts("#{i}: s = #{s}");<br />
ary.push(i);<br />
}<br />
<br />
var list lst;<br />
for (var i = 0; i < ary.size(); ) {<br />
lst.push(ary[i]);<br />
i += 2; //приращение вынесено в тело<br />
}<br />
<br />
var str = '[';<br />
for (var li = lst.begin(); ; ++i) {<br />
if (li == lst.end()) //условие вынесено в тело<br />
break;<br />
str += "#{li.object}, ";<br />
}<br />
str[str.size()-2] = "]";<br />
puts(str);<br />
</source><br />
<br />
<br />
;1-8: Для демонстрации возможности частичного указания выражений цикла, приведен пример, в цикле которого отсутствует выражение-инициализатор. Для работы, цикл использует внешнюю переменную. В теле цикла считается сумма чисел от 0 до 9; результат накапливается опять же, во внешней переменной ''s''. Далее, в массив ''ary'' добавляются текущие значения управляющей переменной, то есть ''i''. <br />
<br />
;10-14: В теле этого цикла, мы копируем каждый нечетный элемент массива ''ary'' в список. Обратите внимание на выражение приращения (в данном случае оно вынесено в тело цикла), которое на каждой итерации добавляет к переменной индекса 2.<br />
<br />
;16-23: Для отображения содержимого списка в удобной для восприятия форме, мы используем строковую переменную, в котороую последовательно записываются элементы массива, отделенные друг от друга запятой. Здесь, в качестве управляющей переменной, выступает [[итератор]] списка ''lst'', который создается в выражении-инициализаторе как локальная переменная цикла.<br />
<br />
=== Цикл foreach ===<br />
<br />
В некоторых языках программирования присутствует специальная синтаксическая конструкция цикла <tt>'''foreach'''</tt>. Как правило, она применяется в случаях, когда необходимо перебрать все значения некоторой коллекции и выполнить код, дословно, "для каждого из значений". <br />
<br />
Учитывая то, что язык K++ ориентирован на использование [[Блоки|блоков]], непосредственная надобность в такой конструкции отпадает, поскольку ее функционал с успехом может быть реализован и даже превзойден стандартными методами коллекций, такими как <tt>each()</tt>, <tt>each_pair()</tt> и <tt>each_with_index()</tt>. Первый метод делает ровно то же самое что и традиционная конструкция <tt>'''foreach'''</tt>, второй вызывает блок, передавая ему пару из индекса и соответствующему индексу элемента из коллекции. Третий метод передает только сам индекс.<br />
<br />
Тем не менее, в целях удобства программистов, была создана системная функция, с названием <tt>foreach()</tt>, которая ведет себя подобно оператору. На деле она всего лишь вызывает метод <tt>each()</tt> у передаваемой ей коллекции:<br />
<source lang="kpp"><br />
function void foreach(const collection, block b) {<br />
collection.each(b);<br />
}<br />
</source><br />
<br />
Как видите, в качестве первого аргумента передается [[Переменные#Нетипированные (динамические) переменные|динамическая переменная]], которая используется для вызова соответствующего метода. В целом, рекомендуется использовать прямой вызов метода, тем более что он является более эффективным, и позволяет писать составные конструкции прямо в одном выражении (см. примеры в главе [[Блоки]]).<br />
<br />
'''Примечание:''' поскольку, с точки зрения языка К++, <tt>'''foreach'''</tt> не является синтаксической конструкцией, ее следует вызывать подобно функции. Это значит, что после закрывающей фигурной скобки должна стоять точка с запятой:<br />
<source lang="kpp"><br />
var ary = [1, 2, 3];<br />
var p = 1;<br />
foreach (ary) { |x|<br />
p *= x;<br />
}; <br />
</source><br />
<br />
=== Управление циклами ===<br />
<br />
Как уже было сказано ранее, в некоторых случаях может потребоваться изменить поведение циклов по умолчанию. Это реализуется с помощью ключевых слов <tt>'''break'''</tt> и <tt>'''continue'''</tt>.<br />
<br />
Ключевое слво <tt>'''break'''</tt> применяется для принудительного завершения выполнения цикла и передачи управления коду, следующему за циклом. Напротив, ключевое слво <tt>'''continue'''</tt> применяется для прерывания текущей итерации и мгновенного перехода к началу следующей. В случае цикла <tt>'''while'''</tt>, это просто будет означать переход на код проверки условия; в случае цикла <tt>'''for'''</tt> — переход на код приращения управляющей переменной, с последующим переходом на код проверки условия.<br />
<br />
Например, цикл <tt>'''for'''</tt> по умолчанию является циклом с предпроверкой условия. То есть, сначала проверяется условие, а затем либо выполняется очередная итерация, либо управление переходит за пределы цикла. Чтобы сделать из него цикл с постпроверкой условия, достаточно вынести код проверки условия в конец тела цикла:<br />
<source lang="kpp"><br />
for (/*инициализатор*/; ; /*приращение*/) {<br />
/* тело цикла */<br />
if (/*условие*/)<br />
break;<br />
}<br />
</source><br />
<br />
Теперь, тело цикла будет выполнено как минимум один раз, после чего будет проверено условие. По умолчанию, пустое выражение условия (в заголовке цикла) означает истину. То есть, цикл без граничных условий будет выполняться вечно, что подводит нас к идее бесконечных циклов:<br />
<br />
=== Понятие бесконечного цикла ===<br />
<br />
В некоторых случаях бывает необходимо выполнять определенные, повторяющиеся действия большое количество раз, причем, заранее неизвестно, сколько итераций необходимо выполнить. Таких задач в программировании довольно много. Как правило это различные сервисные алгоритмы, например такие, как коды обработки входящих клиентских подключений, различные системы контроля и т. д. В них существует как минимум одно место которое должно выполняться в течение всего времени, пока работает программа.<br />
<br />
Для реализации подобных алгоритмов, применяют ''бесконечные циклы'', которые образуются из обычных, ''конечных'' путем изменения условия их выполнения:<br />
<source lang="kpp"><br />
//бесконечный цикл while:<br />
while (true) {<br />
/* тело бесконечного цикла */<br />
}<br />
<br />
//бесконечный цикл for:<br />
for (;;) {<br />
/* тело бесконечного цикла */<br />
}<br />
</source><br />
<br />
В первом случае, мы видим, что выражение условия цикла <tt>'''while'''</tt> представляет собой булеву константу <tt>'''true'''</tt>. Разумеется, ее значение постоянно и соответствует истине. Поскольку цикл <tt>'''while'''</tt> повторяется до тех пор, пока значение условного выражения истинно, получается, что этот цикл будет выполняться бесконечно.<br />
<br />
То же самое происходит и с циклом <tt>'''for'''</tt>. Как уже было сказано выше, пустое выражение условия цикла соответствует истине, поэтому этот цикл тоже никогда не завершится.<br />
<br />
Разумеется, подлинно бесконечные циклы никому не нужны. Компьютеры хоть редко, но все же выключаются, например для обновления программного обеспечения, или улучшения материальной базы. Соответственно программы должны иметь возможность "аккуратного" выключения своими силами. Для этого, в тело бесконечных циклов добавляется код, проверяющий некоторое внешнее условие, которое становится истинным тогда, когда требуется завершить выполнение, и прерывающий цикл с помощью ключевого слова <tt>'''break'''</tt>. <br />
<br />
'''Примечание:''' при разработке закольцованных алгоритмов следует быть очень осторожным и необходимо учитывать их особенности. Вот некоторые соображения, которые помогут вам избежать ошибок в реализации программ:<br />
<br />
# Действительно ли требуется бесконечный цикл? Возможно задача может быть решена обычным, итеративным образом.<br />
# Реализован ли корректный код прерывания цикла? Изменяется ли где-то проверочное условие? Бывает, что программисты либо забывают реализовать код прерывания цикла, либо забывают установить условие прерывания где то в другой части программы, что в конечном счете, приводит к неуправляемым программам.<br />
# Насколько "прожорлив" код, выполняющийся в теле цикла? Может получиться так, что код находящийся в теле цикла, будет захватывать все доступные вычислительные ресурсы системы, поскольку не ограничен в потребностях. В таком случае, вся система станет нестабильной и будет плохо реагировать на действия пользователя. Чтобы этого избежать, необходимо реализовывать дополнительные механизмы, вроде механизма событий (код ожидает некоторое событие после чего продолжает работу), либо вставлять задержку, достаточную, чтобы другие процессы могли своевременно выполняться.<br />
<br />
=== Взаимозаменяемость циклических структур ===<br />
<br />
Как вы уже наверное заметили, одни и те же задачи в языке К++, так же как и в многих других языках, могут быть решены несколькими способами. Это дает большую свободу программисту и способность решать задачи наиболее подходящим и удобным в данный момент образом.<br />
<br />
То же самое относится и к циклам. Фактически, низкоуровневая реализация любого цикла сводится к операциям вычисления значений и соответствующим переходам. В этом смысле, цикл <tt>'''for'''</tt> отличается от цикла <tt>'''while'''</tt> только тем, что в его синтаксисе уже заранее предусмотрены некоторые типовые операции, присущие большинству циклов.<br />
<br />
Приведем примеры, показывающие взаимозаменяемость циклов:<br />
{| width=65% align=center<br />
| <source lang=kpp><br />
var s = 0;<br />
for (var i = 0; i < 10; ++i)<br />
s += i;<br />
<br />
<br />
<br />
<br />
var myObject = new MyClass;<br />
while (!myObject.Compacted)<br />
myObject.Compact();<br />
<br />
//бесконечный цикл while<br />
while (true) {<br />
var need_stop = ProcessEvents();<br />
if (need_stop)<br />
break;<br />
}<br />
</source><br />
| <source lang=kpp><br />
//цикл while в роли цикла for:<br />
var i = 0;<br />
var s = 0;<br />
while (i < 10)<br />
s += i++;<br />
<br />
//цикл for в роли цикла while<br />
var myObject = new MyClass;<br />
for ( ; !myObject.Compacted; )<br />
myObject.Compact();<br />
<br />
//бесконечный цикл for<br />
for (;;) {<br />
var need_stop = ProcessEvents();<br />
if (need_stop)<br />
break;<br />
}<br />
</source><br />
|}<br />
<br />
Мы видим, что один и тот же алгоритм можно реализовать как с помощью цикла <tt>'''while'''</tt>, так и с помощью <tt>'''for'''</tt>. Конечно, некоторые из указанных примеров выглядят, мягко говоря, странно. Это и понятно, — каждый из циклов создавался для решения определенных задач и был ориентирован именно на них. Цикл <tt>'''for'''</tt> создавался для задач, в которых присутствует управляющая переменнаяя, в то время как цикл <tt>'''while'''</tt> — для задач общей логики. Какой из циклов применять в каждом случае, и какой из них в этом случае является удобнее, должен решать сам программист.</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E%D1%82%D0%B2%D0%B5%D1%82%D1%8B_%D0%BA_%D1%83%D0%BF%D1%80%D0%B0%D0%B6%D0%BD%D0%B5%D0%BD%D0%B8%D1%8E_(%D0%BE%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D1%8C_%D0%B2%D0%B8%D0%B4%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8)Ответы к упражнению (область видимости)2013-07-13T11:42:25Z<p>Raw mat: Откат правок DanielBell91 (обсуждение) к версии Korvin</p>
<hr />
<div><source lang="kpp" line="1"><br />
function f(const int p) {<br />
var x = 1, k = 2;<br />
print("x = #{x}, k = #{k}\n"); //(1) x = 1, k = 2<br />
<br />
{<br />
x = 5;<br />
var k = 3;<br />
print("x = #{x}, k = #{k}\n"); //(2) x = 5, k = 3<br />
}<br />
print("x = #{x}, k = #{k}\n"); //(3) x = 5, k = 2<br />
<br />
if (p > 5) {<br />
print("x = #{x}\n"); //(4) x = 5<br />
} else {<br />
var x = 10;<br />
var y = "hello";<br />
print("x = #{x}, #{y}\n"); //(5) x = 10, y = hello<br />
x = 6;<br />
}<br />
print("x = #{x}\n"); //(6) x = 5<br />
print("#{y}"); //(7) ошибка времени компиляции (переменная не объявлена)<br />
}<br />
</source></div>Raw mathttp://man.deeptown.org/index.php/%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D1%8B_%D0%B8_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8BКлассы и объекты2013-07-13T11:40:24Z<p>Raw mat: </p>
<hr />
<div>__TOC__<br />
<br />
== История развития ООП ==<br />
<br />
Для того чтобы понять, что же такое классы и объекты, сперва необходимо проследить историю развития программирования. А конкретнее, историю возникновения концепции ООП. Автор верит, что знание истории возникновения тех или иных мыслей и идей может помочь читателю осознать необходимость нововведений, и главное — их преимуществ перед существовавшими в то время решениями.<br />
<br />
=== Возникновение языков программирования ===<br />
<br />
На заре зарождения вычислительных машин их приходилось программировать поистине "вручную". Все, что было в руках программиста это пульт управления ЭВМ. На шестнадцатеричной клавиатуре (а еще раньше на пульте с тумблерами) программист задавал некоторый адрес ячейки памяти, затем он мог либо выполнить операцию чтения — тогда на табло появлялись цифры, соответствующие значению ячейки памяти, либо операцию записи — при этом, по указанному адресу записывалось значение, набранное на клавиатуре данных. Затем, программист переходил к следующей ячейке, и так повторялось до тех пор, пока в память ЭВМ не была внесена вся программа. На программистах (точнее, на операторах ЭВМ) лежала огромная ответственность! Одна ошибка, один неверно установленный переключатель или одна пропущенная команда неминуемо вели к ошибкам в работе программы, а, следовательно, и к ошибкам в расчетах. Могли потребоваться недели, и даже месяцы на поиск этой ошибки и на ее исправление! Естественно, ни о каких языках программирования тогда не могло идти и речи.<br />
<br />
=== Появление ассемблера ===<br />
<br />
Впоследствии, программисты смекнули, что команды можно записывать в виде мнемонических обозначений или мнемоник — то, что раньше применялось только для удобства записи на бумаге — было стандартизировано и приспособлено как ''язык'' общения человека и ЭВМ. Так появился первый язык программирования — ''язык ассемблера''. Конечно, языком его можно назвать с некоторой натяжкой, ведь он не обеспечивал и десятой доли тех возможностей (вроде автоматического разбора арифметических выражений), которые мы привыкли ассоциировать с языками программирования. Тем не менее, ассемблер выполнял свою главную и основную функцию — избавлял программиста от необходимости работать с памятью (и адресами) напрямую. Вместо этого, программист записывал свои команды в стандартной форме, понятной ЭВМ. Далее выполнялась программа ''транслятор'', которая преобразовывала исходный текст программы в поток машинных команд, которые уже можно исполнять.<br />
<br />
=== Концепция языка высокого уровня ===<br />
<br />
...С увеличением сложности программ, программировать на ассемблере становилось все сложнее и сложнее. Ввиду естественных ограничений человеческой памяти и внимания, написание программ и их отладка стали настолько сложными, что люди всерьез подошли к рассмотрению идеи языка высокого уровня — некоторой системы обозначений и абстрактных команд, которая позволила бы записывать программы в абстрактной форме, не заботясь о том, как располагать в памяти код и данные, как их структурировать и т. д. Всю эту работу брал на себя компилятор. Кроме того, он обеспечивал программиста удобным способом записи математических выражений — в естественной форме. При этом, компилятор сам "разворачивал" эти выражения в наборы инструкций ассемблера, попутно подставляя значения констант и адреса переменных. Это дало возможность программистам записывать формулы вычислений в натуральном виде, что уменьшало трудозатраты, ускоряло написание программ и уменьшало вероятность ошибок. Тем не менее, многие авторитеты того времени очень негативно отзывались о языках высокого уровня. В то время языки были довольно несовершенными и генерировали "ужасный" с точки зрения программистов код. Код был не оптимален, занимал огромное по тем временам количество памяти и работал медленнее, чем та же программа, написанная на ассемблере. Смешно сказать, но в то время многие не верили в то, что будущее за ЯП высокого уровня; их считали не более чем игрушкой для "чайников", возжелавших вообразить себя настоящими программистами. <br />
<br />
Но время шло, количество приверженцев нового подхода постоянно увеличивалось. Сами же компиляторы становились все более мощными и генерировали все более компактный и оптимальный код. Дошло до того, что компилятор с оптимизатором в некоторых случаях генерировал код, более качественный, чем это делал программист. С этого момента ЯП высокого уровня заняли свое место в истории и в инструментарии любого разработчика.<br />
<br />
=== Структурное программирование ===<br />
<br />
С развитием языков программирования появились новые концепции и новые парадигмы программирования. От линейного моноблочного программирования, при котором программа писалась единым "куском" от начала до конца, перешли к программам модульным и структурным. При них программа представляла уже совокупность процедур (функций), которые вызывали друг друга в ходе работы программы. Процедуры представляли собой подпрограммы, решающие отдельные частные задачи. При этом код получался более читаемым, и облегчалась его отладка. <br />
<br />
Опять же, в ходе усложнения решаемых задач и, вследствие этого, увеличения количества переменных с которыми приходилось работать программисту, возникла идея группировки некоторых переменных в группы или структуры. Структуры формировались по назначению и содержали в себе переменные, имеющие отношение к одной и той же сущности. Это значительно повысило читаемость программ и уменьшило количество ошибок в них.<br />
<br />
=== Объектно-ориентированное программирование ===<br />
<br />
Ну и наконец, одна светлая голова додумалась до мысли: "а что если в структурах группировать не только переменные, но и сами процедуры которые должны работать с ними?". В результате получилось то, что мы сейчас называем классом — то есть, некоторая обособленная функциональная сущность, которая сама хранит свои данные, а главное сама умеет их обрабатывать. Теперь программисту не нужно помнить, какая из процедур отвечает за некоторое действие над такими-то переменными — он просто берет объект и работает с ним. Все что происходит с объектом внутри — это его личное дело.<br />
<br />
Последним шагом к современному пониманию программ явились концепции [http://ru.wikipedia.org/wiki/Полиморфизм_(программирование) полиморфизма], [http://ru.wikipedia.org/wiki/Инкапсуляция_(программирование) инкапсуляции] и [http://ru.wikipedia.org/wiki/Наследование_(программирование) наследования]. Не будем пока углубляться в суть этих понятий, отметим только, что их введение сформировало современное понимание объектно-ориентированного программирования.<br />
<br />
При написании программы на объектно-ориентированном языке, программист строит математическую модель взаимодействия различных сущностей. Каждая из сущностей это свой мир, у которого есть свои законы и особенности. При этом сущности могут быть как конкретные, вроде "сетевой интерфейс" или "файл", так и совершенно абстрактные, например "отношение" или "ошибка". Программист описывает каждую из сущностей в отдельности, обособлено от остальных. Вся необходимая для работы информация хранится внутри, а для взаимодействия с внешним миром предусмотрен ''интерфейс'' — некоторая совокупность ''свойств'' данной сущности (отражающих ее внутреннее состояние) и способов взаимодействия с ней — ''методов''.<br />
<br />
В ходе работы программы сущности могут взаимодействовать, читая и записывая свойства и вызывая методы друг друга, использовать друг друга как подсистемы, порождать новые сущности и т. д. Получается, что при написании программы, программист переносит свое внутреннее представление того как он видит эту программу, то из чего она состоит и как отдельные ее части взаимодействуют. Теперь не приходится адаптировать свое понимание проблемы к конкретным инструментальным средствам и возможностям языка программирования (конечно, это все же происходит, но уже гораздо менее заметно).<br />
<br />
В терминах современных языков программирования такие сущности называются ''классами'', в смысле ''классами сущностей''. А отдельные представители этих классов называются ''экземплярами'', ''инстанциями'' (на английский манер) или ''объектами''. Более подробно, различие между классами и объектами будет рассмотрено ниже.<br />
<br />
<br />
Итак, любой современный объектно-ориентированный язык оперирует понятиями классов и объектов. Точно такой же подход нашел свое применение в нашей виртуальной машине. Основой всей платформы Gide является объектно-ориентированный принцип. Причем, в этом смысле она является более объектно-ориентированной, нежели традиционные ЯП вроде C++. В C++ существуют понятия элементарных типов. Это сделано в целях производительности и было продиктовано архитектурой самого языка. В классических языках программирования, элементарные типы, так или иначе, отражают сущности из "реального мира". Например, целочисленные типы int и short соответствуют 32х и 16ти разрядным регистрам процессора, указатели и строки соответствуют представлению данных в памяти и т. д. В Gide это не так. Все с чем оперирует виртуальная машина — это объекты. Соответственно, не существует понятия элементарных типов (просто нет критерия, который бы позволил отделить одно от другого). <br />
<br />
Язык K++ в полной мере наследует идеологию Gide. Скажем, для него нет отличия между типом <tt>int</tt> (целое число) и некоторым пользовательским классом <tt>MyWeirdClass</tt>: везде, где можно использовать <tt>int</tt>, можно использовать <tt>MyWeirdClass</tt> и наоборот. Более того, это позволяет работать с системными классами так же, как с пользовательскими! Например, ничто не мешает унаследовать свой класс от класса <tt>int</tt>, равно как ничто не мешает определить математические операторы для класса <tt>MyWeirdClass</tt> и использовать объекты этого класса в арифметических выражениях. При этом изменится логика работы всего языка. К примеру, после добавления некоторого метода к классу int можно будет вызывать методы у всех его экземпляров, даже тех, что представлены числовыми константами внутри самого языка!<br />
<br />
== Понятие класса ==<br />
<br />
Что такое ''класс'' проще всего объяснить на примерах. Представьте, что вас спрашивают "что такое яблоко?". Скорее всего, вы ответите что-то вроде: "яблоки, это вкусные плоды, растущие на деревьях — яблонях; они бывают разных цветов и размеров". Заметьте, что когда мы описываем ''яблоки как понятие'', мы не имеем в виду некоторый конкретный объект, а скорее описываем наше обобщенное представление о них. Если же вас попросят описать совершенно конкретное яблоко, лежащее на блюдечке перед вами, вы будете говорить о нем по другому: "это яблоко, оно красное, сочное, судя по всему спелое. С черенком, на котором остался листик, и маленькой червоточинкой". <br />
<br />
Разница заключается в том, что когда вы говорили ''о яблоках'', вы описывали свое представление яблок, как ''класса'' объектов. Когда вы описывали ''яблоко'', то вы имели в виду конкретный ''экземпляр'', или ''объект''. Говоря о классе, вы можете описать только те свойства, что принадлежат всем яблокам; когда же вы описываете объект, то в первую очередь имеете в виду его индивидуальные особенности (свойства). Тем не менее, описание объекта начинается с упоминания его класса ("это яблоко,..."), а затем уже свойств объекта (ведь "сочным и спелым" может быть и апельсин). Это важная особенность объектно-ориентированного подхода. <br />
<br />
Другой пример: если вас попросить "представьте дерево", то вы либо представите некоторое совершенно абстрактное, усредненное дерево, либо попросите уточнить, какое именно дерево имеется в виду. Ваше сознание из имеющейся информации смогло уяснить только самые общие сведения о классе. Но этой информации не достаточно, для более детального описания. Это тоже важно, поскольку в этом простом примере кроется сущность механизма наследования — постепенного уточнения классами-потомками общих черт своих предков. Таким образом, и яблоня, и груша — деревья. Но яблони отличаются от груш. Получается, что классы ''яблони'' и ''груши'' имеют общего предка — класс ''дерево''.<br />
<br />
Таким образом, понятия классов и объектов это не математическая абстракция, а скорее часть нашего восприятия мира, того как мы мыслим.<br />
<br />
<br />
Из примеров выше мы смогли уяснить следующее:<br />
<br />
* Классы, это некоторые абстрактные сущности, задающие общие черты своих объектов<br />
* Все объекты одного класса похожи друг на друга, но имеют некоторые индивидуальные особенности<br />
* Классы могут наследоваться, расширяя набор свойств класса родителя своими собственными<br />
<br />
Перейдем теперь ближе к основной теме нашего повествования, а именно языку К++:<br />
<br />
С точки зрения языка, ''класс'' представляет собой набор следующих элементов:<br />
* ''полей'', т.е. переменных, хранящих индивидуальные особенности объектов, <br />
* ''методов'', т.е. функций, определяющих поведение данного объекта;<br />
* ''свойств'', определяющих взаимодействие других объектов с объектами данного класса.<br />
<br />
''Поля'' — это переменные, которые относятся к некоторому конкретному экземпляру нашего класса. Каждый экземпляр имеет свою копию набора переменных, таким образом, они могут хранить свое состояние (например, показатель "спелости", в примере с яблоками). Эти переменные доступны только самому классу, доступ извне для них запрещен. Для того чтобы частично разрешить этот доступ, применяются ''свойства''. Сами свойства будут описаны позже, здесь стоит отметить только то, что свойство может быть доступно "только на чтение", "только на запись" или "и на чтение и на запись". Свойство может быть связано либо с некоторым полем, либо с методом. Например, если свойство доступно "только на чтение" то его можно использовать для получения значения, но не для его записи (то есть, такое свойство не может фигурировать в качестве [[lvalue]]).<br />
<br />
''Методы'' — это тот самый, связанный с данными код (вспомните [[Классы и объекты#История развития ООП|историю ООП]]) который, естественно, может работать с переменными объекта (то есть с полями) и служит для описания поведения данного класса объектов.<br />
<br />
Класс может иметь одного или нескольких родителей (опять же, подробнее об этом см. ниже)<br />
<br />
Методы и свойства класса могут находиться в различных областях видимости. Это обеспечивается с помощью [[Спецификаторы доступа|спецификаторов доступа]]:<br />
<br />
* <tt>'''private'''</tt> — Частная собственность! Видимость только внутри методов данного класса<br />
* <tt>'''protected'''</tt> — "Семейная реликвия", доступ внутри методов данного класса и всех его дочерних классов<br />
* <tt>'''public'''</tt> — видимость и доступ для всех<br />
<br />
'''Примечание:''' По умолчанию методы имеют видимость <tt>'''private'''</tt>, в то время как свойства — <tt>'''public'''</tt>.<br />
<br />
Приведем, наконец, пример объявления класса:<br />
<br />
<source lang="kpp" line="1"><br />
class MyWeirdClass {<br />
var m_x = 0; // поле m_x, изначально проинициализированное нулем<br />
const m_y = 1; // поле-константа m_y<br />
<br />
// методы класса<br />
public const function int get_mul() { return m_x * m_y; }<br />
public function void set_mul(int x) { m_x = x / m_y; }<br />
<br />
// свойство класса<br />
public property mul read get_mul write set_mul;<br />
}<br />
</source><br />
<br />
<br />
;1: Как у любого нормального разумного существа, у класса есть "голова" и "тело". Ключевое слово <tt>'''class'''</tt> начинает объявление класса. Далее за ним следует [[идентификатор]] имени класса, после чего идет тело класса.<br />
<br />
;3-4: Здесь мы видим объявление двух полей класса — переменной ''m_x'' и константы ''m_y'', которые, подобно обычным переменным инициализируются тут же, на месте объявления (камень в огород C++). Зачем нужны поля-константы, описано в главе [[Константы]]. <br />
<br />
;7-8: Для доступа к состоянию объекта, определены два метода: ''аксессор'' <tt>get_mul()</tt> и ''мутатор'' <tt>set_mul()</tt>. Подобные конструкции применяются настолько часто, что им были даны специальные имена. Как видно из названия, первый метод дает доступ к значению, второй изменяет или мутирует его.<br />
<br />
;11: Завершается объявление класса объявлением свойства ''mul'', которое связывается с аксессором и мутатором. Думаю, читатель уже догадался, что это свойство типа "чтение и запись". Таким образом, при обращении к свойству на чтение, будет вызван аксессор, а результат его выполнения будет возвращен в качестве значения свойства. И, наоборот, при попытке записать в свойство некоторое значение, будет вызван мутатор, в качестве аргумента которому будет передано это самое значение, а уж сам мутатор позаботится о том, чтобы оно было "доставлено по адресу".<br />
<br />
: '''Примечание:''' Зачем нужны такие сложности, и зачем дублировать вроде бы одинаковый функционал, будет описано ниже.<br />
<br />
== Понятие объекта ==<br />
<br />
Собственно, понятие объекта уже много раз было затронуто выше по повествованию. Поэтому здесь приведем лишь небольшое определение: Под ''объектом'' подразумевается экземпляр того или иного класса, т.е. некоторая сущность, поведение которой задается соответствующим классом.<br />
<br />
Для создания объекта того или иного класса служит оператор <tt>'''new'''</tt>:<br />
<source lang="kpp"><br />
var myWeirdObject = new MyWeirdClass;<br />
</source><br />
<br />
Здесь мы видим типичную конструкцию объявления переменной, однако в инициализаторе переменной находится всего один оператор, за которым следует [[идентификатор]] имени класса, экземпляр которого мы хотим создать.<br />
<br />
== Наследование ==<br />
<br />
Под ''наследованием'' классов понимается механизм такого создания (объявления) класса, при котором он расширяет функционал одного или нескольких уже существующих классов (родителей). Вспомните пример с деревьями. Когда мы говорим, что класс ''яблоня'' наследуется от класса ''дерево'' — это значит что ''яблоня'' унаследует все свойства своего класса-родителя, некоторые из которых он может изменить, ну и дополнить своими собственными свойствами. Опыт нам подсказывает, что у любого дерева есть листья (для простоты не будем вспоминать про хвойные), однако не любое дерево плодоносит яблоками. Если же рассмотреть сами яблоки, то можно сказать, что класс ''яблоко'' унаследован от класса ''фрукт'', который определяет что фрукты (и соответственно яблоки) должны расти на деревьях.<br />
<br />
Таким образом, наследование гарантирует, что к дочерним классам применимы все операции, доступные в родительском классе: любой алгоритм, работающий с объектами родительского класса, может работать с объектами его дочерних классов. <br />
<br />
Для задания наследования, в объявлении класса следует указать ключевое слово <tt>'''extends'''</tt>, за которым необходимо перечислить список идентификаторов классов-родителей, разделяя их запятыми:<br />
<br />
<source lang="kpp" line="1"><br />
// коробка<br />
class Box { <br />
// из чего сделана коробка?<br />
public const function string material() { return "Картон"; }<br />
<br />
// что в коробке?<br />
public const function string contents() { return "Пусто"; }<br />
}<br />
<br />
// коробка с картошкой<br />
class BoxWithPotatoes extends Box { <br />
public const function string contents() { return "Картошка"; }<br />
}<br />
<br />
function OutputBox(const Box b) {<br />
print("Материал: " + b.material() + ", содержит: " + b.contents() + "\n");<br />
}<br />
<br />
export function main() {<br />
var b1 = new Box;<br />
var b2 = new BoxWithPotatoes;<br />
OutputBox(b1); // Материал: Картон, содержит: Пусто<br />
OutputBox(b2); // Материал: Картон, содержит: Картошка<br />
}<br />
</source><br />
<br />
<br />
;2-8: В этом примере мы создаем класс <tt>Box</tt>, который представляет собой некоторую коробку. Мы определяем ее свойства, такие как ''материал'' и ''содержимое''.<br />
<br />
;11-13: Далее мы определяем класс <tt>BoxWithPotatoes</tt> (''коробка с картошкой''), который наследуется от класса <tt>Box</tt>, и тем самым заимствует свойства материала и содержимого, но первое он переопределяет (в случае методов это называется ''перекрытием'') собственным методом.<br />
<br />
;15-17: Мы определяем некоторую [[Функции|функцию]] для работы с нашими классами, которая будет отображать их состояние. Заметьте, что в качестве [[Функции#Аргументы|аргумента функции]] передается экземпляр класса <tt>Box</tt>, то есть класса-родителя. При этом мы предполагаем, что любой класс, унаследованный от базового класса, будет обладать необходимым нам интерфейсом, а именно методами получения информации о свойствах (аксессорами мы их не называем, потому что они не связаны с конкретным полем; это было бы неверно).<br />
<br />
;19-24: Объявляется функция <tt>main()</tt>, внутри которой и происходит самое интересное. Сначала мы создаем два экземпляра ''b1'' и ''b2'' классов <tt>Box</tt> и <tt>BoxWithPotatoes</tt> соответственно. А затем, вызываем вышеописанную функцию <tt>OutputBox()</tt>, которая отображает содержимое. Вывод в терминал (написан в комментарии к вызову) показывает, как это все работает.<br />
<br />
'''Примечание:''' при использовании множественного наследования существует одно серьезное ограничение: его нельзя применять для наследования от классов стандартной библиотеки. Это ограничение связано с архитектурой платформы Gide. Однако его можно обойти с помощью создания [[Класс-обертка|классов-оберток]] для системного класса, с последующим наследованием от него нового класса.<br />
<br />
== Методы ==<br />
<br />
''Метод'' — это некоторый код, связанный с объектом и управляющий его поведением. Управление может заключаться в изменении переменных состояния объекта (полей), либо выполнением некоторых операций над ними.<br />
<br />
При объявлении метода могут быть указаны следующие ключевые слова, в указанном порядке:<br />
* <tt>'''private'''</tt>, <tt>'''protected'''</tt> или <tt>'''public'''</tt> — определяют область видимости метода<br />
* <tt>'''static'''</tt> — указывает, что метод является ''статическим'' (см. ниже)<br />
* <tt>'''const'''</tt> — метод не изменяет объект<br />
* <tt>'''function'''</tt>, <tt>'''operator'''</tt> или <tt>'''constructor'''</tt> указывает, что объявляется: ''метод'', ''[[Операторы|оператор]]'' или ''конструктор'' (см. ниже)<br />
* <tt>'''const'''</tt> — метод возвращает результат, который нельзя изменять<br />
<br />
После этого указывается тип, возвращаемый методом. Если он опущен — возвращается [[Переменные#Нетипированные (динамические) переменные|динамическая переменная]]; если вместо типа указано ключевое слово <tt>'''void'''</tt> — метод не возвращает результата. Следом за типом идет имя метода, затем — перечисление [[Функции#Аргументы|аргументов]] в скобках. Подробнее об объявлении функций и передаче параметров, можно прочитать в главе [[Функции]].<br />
<br />
В теле метода доступны все поля, методы и свойства данного класса и его предков. Например, в нижеприведенном коде, метод <tt>F2()</tt> вызывает метод <tt>F1()</tt> для того же объекта:<br />
<source lang="kpp" line="1"><br />
class MyClass {<br />
public const function string F1() { return "smth"; }<br />
public const function string F2() { return F1(); }<br />
}<br />
</source><br />
<br />
<br />
=== Статические методы ===<br />
<br />
''Статический метод класса'' — это метод, относящийся к данному классу, но не объекту этого класса. Т.е. это некоторая вспомогательная для данного класса функция.<br />
<br />
При объявлении статического метода нужно указать ключевое слово <tt>'''static'''</tt>.<br />
<br />
В теле статического метода нет возможности напрямую обращаться к другим методам данного класса, т.к. статическому методу недоступен объект класса. Фактически, единственным отличием статического метода от обычной функции является то, что такой метод может обращаться к защищенным полям и методам класса некоторого другого объекта. Подобный подход широко применяется при написании ''конструкторов'' (см. ниже).<br />
<br />
Пример:<br />
<source lang="kpp"><br />
class MyClass {<br />
public static function string Info() { return "Я MyClass!"; }<br />
}<br />
<br />
function f() {<br />
// Вызов статического метода:<br />
var myClassInfo = MyClass.Info();<br />
}<br />
</source><br />
<br />
=== Конструкторы ===<br />
<br />
<font color="red">'''Внимание: информация в этом разделе устарела. Необходимо обновление'''</font><br />
<br />
<br />
В ходе написания программы часто приходится создавать новые объекты. При этом объекту требуется установить некоторое начальное состояние. Разумеется, это выполняется либо путем написания инициализаторов соответствующих полей, либо значения полям присваиваются явным образом. Но что делать, если объект может иметь несколько начальных состояний? То есть, в зависимости от некоторых условий, одним и тем же полям объекта могут быть присвоены различные наборы значений. Тут уже одними инициализаторами не обойтись. Присваивать значения можно прямо в коде программы, однако это не очень красивое решение, поскольку один и тот же объект может создаваться в нескольких местах программы и придется копировать один и тот же участок кода, инициализирующий поля объекта. Недостаток этого подхода в том, что при большом количестве полей, программист может забыть проинициализировать некаторе поле, либо при изменении условий инициализации он может изменить их только в одном месте программы, забыв, что объект может создаваться и в других местах. Более разумным является подход, при котором программист пишет функцию, инициализирующую поля объекта. При этом весь код собирается в одном месте и вероятность ошибок значительно понижается. <br />
<br />
Конструкторы развивают эту идею, дополняя код инициализации полей кодом создания самого экземпляра объекта. То есть, в конструкторе собирается весь код, относящийся к созданию инстанции класса. Итак, ''конструктор класса'' — это специальный метод, инициализирующий объект класса. С точки зрения языка, конструктор — это статический метод класса, возвращающий экземпляр данного класса.<br />
<br />
Таким образом, следующие объявления в рамках класса эквивалентны:<br />
<source lang="kpp"><br />
public constructor Create();<br />
public static MyClass Create();<br />
</source><br />
<br />
Тело конструктора чаще всего выглядит следующим образом: сначала создается экземпляр класса при помощи оператора <tt>'''new'''</tt>, затем производятся некоторые действия, инициализующие этот объект, и, наконец, этот объект возвращается в качестве результата. <br />
<br />
В качестве примера приведем пример реализации некоторого абстрактного класса <tt>MyStream</tt>, использующего системную реализацию класса <tt>stream</tt>, и определяющую собственный конструктор:<br />
<br />
<source lang="kpp" line="1"><br />
class MyStream {<br />
var string m_URL;<br />
var stream m_Stream;<br />
public:<br />
static const MODE_READ = 1;<br />
static const MODE_WRITE = 2;<br />
constructor open(const string url, int mode) {<br />
var self = new MyStream;<br />
self.m_Stream.open(url, mode);<br />
self.m_URL = url;<br />
return self;<br />
}<br />
// прочие методы (опущены для краткости)<br />
}<br />
</source><br />
<br />
<br />
;2-3: Объявляются поля ''m_URL'' и ''m_Stream'' нашего класса, которые будут инициализироваться в конструкторе. <br />
<br />
;5-6: В публичной области класса объявляются статические константы ''MODE_READ'' и ''MODE_WRITE'', которые задают желаемый режим доступа к открываемому потоку. Это единственный случай, когда поле класса доступно на прямой доступ извне.<br />
<br />
;7: Объявляется конструктор <tt>open()</tt>, принимающий в качестве параметров [[URL]] ресурса который требуется открыть и число, задающее с помощью вышеописанных констант режим доступа к потоку. В теле конструктора мы создаем инстанцию ''self'' нашего класса. Затем производится попытка открытия потока ''m_Stream'': если операция пройдет успешно, то происходит инициализация оставшегося поля ''m_URL'' и выход из конструктора с возвратом созданной инстанции; если же операция открытия потока провалится, то будет сгенерировано ''[[Обработка исключений|исключение]]'' и выполнение конструктора прекратится (управление будет передано "наверх", вызывающему коду).<br />
<br />
<br />
Использовать наш класс можно примерно так:<br />
<source lang="kpp">var data = MyStream.open("http://www.deeptown.org/index.html", MyStream.MODE_READ);</source><br />
<br />
Обратите внимание на второй параметр функции, где мы передаем константу ''MODE_READ'', объявленную внутри самого класса. Подобный подход, когда константы, используемые при работе с классом, инкапсулируются в тело класса, помогает сконцентрировать код в одном месте и избежать многих ошибок. В результате повышается читаемость кода и не возникает конфликта имен констант.<br />
<br />
=== Абстрактные методы ===<br />
<br />
Случается, что при проектировании интерфейсов классов, возникает желание объявить некоторый набор методов в базовом классе, однако не реализовывать их. Обычно это связано с тем, что базовый класс не располагает достаточным количеством информации или возможностей для реализации этой функции. В таких случаях прменяется спецификатор <tt>'''abstract'''</tt>, говорящий комплиятору примерно следующее: "Это всего лишь объявление прототипа метода; не надо пытаться искать его реализацию ниже и выдавать ошибку". Абстрактные методы реализуются в потомках класса, предоставляя уже конкретный функционал. Тем не менее, существует возможность обращения к таким методам напрямую из базового класса.<br />
<br />
Можно возразить следующее: зачем объявлять прототипы методов, если это не критично для языка? Ведь К++ Не является строго типированным, соответственно необходимость последующей реализации метода можно просто оставить на совести программиста...<br />
<br />
На самом деле это не совсем так. Декларация абстрактного метода помогает вылавливать некоторые ошибки и делает интерфейс класса более четким.<br />
<br />
Во-первых, при использовании такого метода в абстрактных алгоритмах базового класса, компилятор имеет возможность проверять правильность вызова метода и выполнять автоматическое преобразование типов, если это необходимо.<br />
<br />
Во-вторых, программист, изучающий интерфейс класса в качестве кандидата для своего класса-потомка, сразу будет видеть какие методы использует данный базовый класс. Если же абстрактные методы не объявить, то программисту-пользователю придется либо изучать исходники более подробно, либо "курить мануалы". Зачастую, ошибки подобного рода обнаруживаются только во время выполнения программы.<br />
<br />
Наконец, объявление абстрактного метода в базовом классе, позволяет автоматически генерировать особый тип исключения: <tt>[[EAbstractError]]</tt>. Оно возникает в случае, если была произведена попытка вызова метода объявленного абстрактным в базовом классе но не реализованом в используемом потомке. В таком случае сразу ясно, где кроется ошибка. Если бы объявления не было, то возникло бы обычное исключение о том что метод не найден. <br />
<br />
Примечание: Использование абстрактных методов особенно удобно при реализации [[Примеси|примесей]]. Приведем пример стандартной примеси <tt>[[Enumerable|примесь Enumerable]]</tt>:<br />
<br />
<source lang="kpp"><br />
class Enumerable {<br />
public:<br />
abstract const function each(block b);<br />
abstract const function int compare(const x);<br />
<br />
const function bool all(block b = identity_block) {<br />
var result = true;<br />
this.each() { |...|<br />
if (!b.invoke(args())) {<br />
result = false;<br />
break;<br />
}<br />
};<br />
return result;<br />
}<br />
<br />
// (Другие методы примеси)<br />
}<br />
</source><br />
<br />
Примесь Enumerable предоставляет классам коллекций несколько методов для итерации по элементам, выбору, поиску и возможности сортировки. Класс испольщующий примесь должен объявить у себя метод <tt>each()</tt>, который вызывает переданный блок последовательно передавая ему элементы коллекции. Если требуется использовать методы Enumerable::max, min или алгоритм сортировки, то необходимо также реализовать соответствующий метод <tt>compare()</tt>. Примеси эффективны там, где много различных классов могут реализовывать похожее поведение, однако наследовать их все от некотрого общего базового класса нерационально. В некотором роде, примесь это своеобразный "недокласс", экземпляры которого не является полностью самостоятельными сущностями, но поведение методов примеси в рамках другого класса может быть разумным. Так, напирмер вышеописанная примесь <tt>Enumerable</tt> предоставляет большое количество методов для работы с коллекциями (обработки элементов), однако она понятия не имеет, о каких именно элементах идет речь. Примесь не располагает механизмом сохранения и выборки элементов, предполагая что эта функциональность будет получена в результате "симбиоза" с классом, включающим данную примесь.<br />
<br />
В данном случае мы видим, что примесь явным образом объявляет абстрактные методы <tt>each()</tt> и <tt>compare()</tt>. Эти два метода -- единственная внешняя зависимость примеси от класса, ее использующего. <br />
<br />
Таким образом, мы можем использовать примесь в любом классе, который может и не являться коллекцией. В качестве примера можно привести такой код:<br />
<source lang="kpp"><br />
class MyClass extends Enumerable {<br />
public const function each(block b) {<br />
b("hello");<br />
b("to");<br />
b("mixin!!!");<br />
}<br />
}<br />
<br />
export function main() {<br />
var c = new MyClass;<br />
var i = 1;<br />
c.collect() { |x| i++ to string + ' ' + x; }.each() { |x| puts(x); }<br />
}<br />
</source><br />
<br />
Нетрудно догадаться, что результатом выполнения такой программы будут следующие строки:<br />
1 hello<br />
2 to<br />
3 mixin!!!<br />
<br />
Обратите внимание, что мы обращаемся с инстанцией нашего класса как с коллекцией -- вызываем методы collect() и итерируем по элементам так, как будто это массив. Основное преимущество примесей в том, что они позволяют писать очень гибкий код одновременно делая его максимально универсальным и годным для повторного использования.<br />
<br />
'''Примечание:''' Метод collect() объявлен в стандартной библиотеке К++ (файл kpp.kpp).<br />
<br />
== Поля ==<br />
<br />
Поле класса — это некоторый объект, используемый объектом данного класса.<br />
<br />
Объявление поля начинается с одного из трех ключевых слов:<br />
* <tt>'''var'''</tt> — объявление "обычного" поля;<br />
* <tt>'''const'''</tt> — данное поле является константой и не может быть изменено;<br />
* <tt>'''mutable'''</tt> — значение данного поля не влияет на состояние объекта, и его можно менять даже в методах, объявленных константными.<br />
<br />
За ключевым словом следует тип поля и [[идентификатор]] его имени. Тип поля может быть опущен. После имени может стоять символ "=" и выражение, инициализирующее значение данного поля. В целом, синтаксис тот же, что и при объявлении обычной переменой или константы:<br />
<br />
<source lang="kpp"><br />
var int m_x;<br />
const m_y = 0;<br />
var m_stream = new stream;<br />
</source><br />
<br />
Тип поля определяется по следующим правилам:<br />
* если тип указан явно, ничего определять не надо;<br />
* если тип не указан, но при объявлении использован инициализатор — типом становится тип результата инициализатора;<br />
* в противном случае, для поля устанавливается [[Переменные#Нетипированные (динамические) переменные|динамический тип]].<br />
<br />
'''Примечание:''' В K++ доступ к полям имеет только объект класса — т.е. фактически, все поля находятся в закрытой (private) области видимости. Для предоставления внешним классам доступа к полям следует использовать ''свойства'' (см. ниже).<br />
<br />
== Свойства ==<br />
<br />
''Свойства'' — это специальные конструкции языка К++, которые позволяют совмещать обращение к данным с вызовом определенного метода. Смысл свойств заключается в том, чтобы программист мог контролировать процесс изменения состояния объекта и своевременно реагировать на это изменение. Свойства бывают доступны на чтение, на запись, или на чтение и на запись одновременно. Это связано с тем, что некоторые свойства объекта (в естественном понимании этого слова), могут предполагать только получение информации о них, другие же могут подразумевать изменение состояния, без возможности чтения. <br />
<br />
В существующих языках программирования, таких как C++ тот же функционал реализуется с помощью вызова специальных методов: ''аксессоров'' и ''мутаторов'', которые используются для получения сведений о некотором свойстве или для записи соответственно. Однако это делает код менее читаемым, особенно в случае мутаторов. Свойства же, позволяют работать с собой подобно обычным полям или объектам, используя операторы. <br />
<br />
Приведем два примера, которые позволят понять смысл свойств и их отличие от обычных полей класса.<br />
<br />
Предположим, что у нас есть класс, отвечающий за чтение состояния некоторого устройства. Допустим, состояние представляется целым числом и должно определяться по мере обращения. Если бы мы писали на языке C++, то мы оформили бы это в виде метода:<br />
<br />
<source lang="cpp"><br />
class MyDevice {<br />
int GetState();<br />
};<br />
</source><br />
<br />
Везде, где нам потребовалось бы читать состояние устройства мы должны были писать что-то типа:<br />
<br />
<source lang="cpp"><br />
int current_state = Device.GetState();<br />
</source><br />
<br />
В случае с К++, чтение свойств осуществляется подобно обычным полям. Перепишем вышеописанный пример на язык К++: <br />
<br />
<source lang="kpp"><br />
class MyDevice {<br />
function int GetState();<br />
property int state read GetState;<br />
}<br />
</source><br />
<br />
Соответственно, обращение к свойству состояния будет выглядеть так:<br />
<br />
<source lang="kpp"><br />
var current_state = Device.state;<br />
</source><br />
<br />
При обращении к свойству <tt>state</tt> будет автоматически вызван метод <tt>GetState()</tt>, результат которого будет возвращен как значение свойства. Вышеописанный пример кому-то может показаться странным, ведь получается, что мы усложнили код класса ради сомнительного выигрыша в коде обращения. На самом деле, в реальных условиях, с настоящими классами, с большим количеством свойств и в сложных выражениях, выигрыш становится куда более заметен. Сравните два примера одного и того же участка кода, один из которых написан на C++, другой на K++. Несмотря на то, что приведенный код тоже взят "с потолка", разница в читаемости уже более заметна. В целом, чем сложнее выражение и чем больше в нем применяется операций присваивания и доступа к полям классов — тем большее преимущество дает использование свойств:<br />
<br />
<source lang="cpp"><br />
Object1.SetStatus(Object2.GetStatus() > 0 ? Object2.GetStatus() : DefaultObject.GetStatus());<br />
printf("object %s (%d) located at %s : status changed to %u", <br />
Object1.GetName(), Object1.GetIndex(), Object1.GetLocation(), Object1.GetStatus());<br />
</source><br />
<br />
<source lang="kpp"><br />
Object1.status = Object2.status > 0 ? Object2.status : DefaultObject.status;<br />
puts("object % (%) located at % : status changed to %", <br />
Object1.name, Object1.index, Object1.location, Object1.status);<br />
</source><br />
<br />
В качестве второго примера мы приведем код, более близкий к реальной жизни. Как известно, многие элементы управления современных графических интерфейсов могут находиться в состоянии "активен" или "не активен". Неактивные элементы не реагируют на действия пользователя (например, кнопки не будут нажиматься) и как правило окрашиваются в оттенки серого (для того чтобы нельзя было их спутать с активными элементами). Естественно, это поведение определяется некоторым полем в объекте элемента управления. В зависимости от его значения, библиотека графического интерфейса будет по-разному обрабатывать и отрисовывать этот элемент управления.<br />
<br />
В языке К++ это поведение можно легко описать, используя двусторонние свойства, то есть такие, которые можно использовать как на чтение, так и на запись. Логично предположить, что чтение такого свойства не должно сказываться на самом элементе управления, в то время как запись в свойство должна дать команду элементу управления изменить свое состояние и соответственно внешний вид. Вот пример описания некоторого абстрактного класса элемента управления:<br />
<br />
<source lang="kpp" line="1"><br />
class Widget {<br />
var m_Enabled = true; //поле, хранящее текущее состояние активности<br />
function void SetEnabled(const value) {<br />
var old_value = m_Enabled;<br />
m_Enabled = value;<br />
if (old_value != m_Enabled)<br />
Invalidate(); //Состояние изменилось, обновляем элемент управления<br />
}<br />
property enabled read m_Enabled write SetEnabled;<br />
//далее идет остальная часть класса, например методы отрисовки<br />
}<br />
</source><br />
<br />
Теперь, если мы унаследуем некоторый класс от данного класса и переопределим соответствующие методы отрисовки, то у класса потомка так же можно будет использовать свойство <tt>enabled</tt>:<br />
<br />
<source lang="kpp"><br />
var myForm = new Form; //создаем окно<br />
var myButton = Button.CreateAtPos(myForm, 10, 10); //добавляем кнопку<br />
myButton.caption = "Click me!"; //устанавливаем подпись<br />
myButton.OnClick += { |x| x.enabled = false; }; //подключаем обработчик события<br />
myForm.Show(); //показываем окно<br />
</source><br />
<br />
Приведенный выше код создаст окно и разместит на нем кнопку (подразумевается, что класс <tt>Button</tt> унаследован от нашего класса <tt>Widget</tt>). Затем устанавливаются свойства кнопки, такие как подпись и [[Блоки|блок]] обработчика события <tt>OnClick</tt>. При нажатии на кнопку она станет неактивной.<br />
<br />
<br />
----<br />
<br />
<br />
В заключение, кратко опишем синтаксис объявления свойства и поясним его. Итак, объявление любого свойства начинается с указания ключевого слова <tt>'''property'''</tt>, после которого идет [[идентификатор]] типа свойства. Тип может быть опущен, тогда для свойства будет определен [[Переменные#Нетипированные (динамические) переменные|динамический тип]]. Затем указывается идентификатор имени свойства. <br />
<br />
Оставшаяся часть зависит от того, какое свойство объявляется:<br />
<br />
* Если объявляется свойство на чтение, то указывается ключевое слово <tt>'''read'''</tt>, после которого идет либо идентификатор имени поля, которое нужно читать, либо имя метода, который должен использоваться как ''аксессор''. В роли аксессора может выступать метод, не принимающий параметров и возвращающий некоторое значение, которое будет возвращаться как значение свойства.<br />
* Если объявляется свойство на запись, то указывается, соответственно ключевое слово <tt>'''write'''</tt> после которого идет либо имя поля, которое нужно записывать, либо имя метода, который должен использоваться как ''мутатор''. В качестве мутатора может выступать метод, принимающий один параметр. Возвращаемое значение игнорируется, так что оно может быть любым.<br />
* Если объявляется свойство, доступное как на чтение, так и на запись, то указываются обе части, причем первой идет часть чтения.<br />
<br />
<br />
'''Примечание:''' Если тип свойства указан явно, то в зависимости от типа поля либо типа возвращаемого значения аксессора, может быть выполнена операция [[Приведение типов|приведения типов]]. Разумеется, если тип поля или результат аксессора неприводим к указанному типу свойства, то будет выдано сообщение об ошибке. Аналогичная ситуация обстоит и с параметром мутатора.<br />
<br />
Кратко, синтаксис объявления свойства можно описать в стиле справки к командам оболочки:<br />
<source lang="kpp"><br />
property [тип] <имя> [read <аксессор|поле>] [write <мутатор|поле>];<br />
</source><br />
<br />
'''Примечание 2:''' Существует альтернативный синтаксис описания аксессоров и мутаторов, при котором код соответствующий им записывается прямо в определении самого свойства. Это выглядит так:<br />
<br />
<source lang="kpp"><br />
class MyClass {<br />
var f = 1;<br />
property int read { f + 1; } write { |v| f = v; };<br />
}<br />
</source><br />
<br />
И аксессор и мутатор, представлены здесь в виде inline конструкций, схожих по описанию (и смыслу) с inline блоками. Аксессор возвращает значение поля ''f'', увеличенное на единицу, в то время как мутатор принимает некоторое значение ''v'' и записывает его в соответствующее поле без каких либо изменений.<br />
<br />
Подобные конструкции могут быть удобны при необходимости реализации свойств-преобразователей, например возвращающих значение некоторого угла в градусах, в то время как в объекте он хранится в радианах. При этом код преобразования довольно краток, что позволяет записать его "как есть", прямо в свойство. Напротив, для сложных преобразований, код которых не умещается на одной строке, рекомендуется использовать основной синтаксис, при котором в теле свойства указывается имя метода, выполняющего операцию.<br />
<br />
== Расширения ==<br />
<br />
Иногда бывает необходимо расширить функциональность некоторого существующего класса без порождения дочернего класса. Это может быть необходимо в тех случаях, когда исходный класс уже используется в коде программы и порождение нового класса нарушило бы спецификацию на интерфейс, либо потребовало значительных изменений в исходных текстах программы. В таких случаях целесообразно применять т. н. ''расширения классов''. Синтаксически, конструкция расширения практически не отличается от конструкции объявления класса, однако методы и свойства, перечисленные в нем, дополняются к исходному классу, то есть наследования не происходит.<br />
<br />
Как правило, расширения применяются к классам [[Стандартной библиотека Gide|стандартной библиотеки]], либо к [[unmanaged код|неуправляемым классам]].<br />
<br />
В качестве примера приведем код, расширяющий функциональность класса <tt>[[Стандартные типы данных#Целые числа|int]]</tt> с помощью свойства <tt>factorial</tt>. Расширения объявляются путем указания ключевого слова <tt>'''extend'''</tt>, после которого указывается [[идентификатор]] имени класса, который следует расширить. Затем идет тело расширения, такое же, как при описании классов:<br />
<br />
<source lang="kpp" line="1"><br />
package intmod_fact;<br />
<br />
extend int {<br />
const function int GetFactorial() {<br />
var result = 1;<br />
for (var x = this; x > 1; x--)<br />
result *= x;<br />
return result;<br />
}<br />
property int factorial read GetFactorial;<br />
}<br />
<br />
export function main() {<br />
puts("Factorial of 10 is " + 10.factorial);<br />
}<br />
</source><br />
<br />
<br />
;3-11: Мы объявляем расширение класса <tt>[[Стандартные типы данных#Целые числа|int]]</tt>, реализованного в [[Стандартная библиотека gide|стандартной библиотеке]]. Добавляется частный метод <tt>GetFactorial()</tt>, и свойство <tt>factorial</tt>, связанное с методом. Как видно из названия, метод рассчитывает факториал числа, которое содержится в объекте. В данном контексте, специальная переменная ''this'' имеет тип <tt>int</tt> и ссылается на сам объект. Таким образом, для числа 10 переменная ''this'' будет равна 10.<br />
<br />
;14: Здесь мы видим применение расширения в действии. Константа 10 на момент компиляции преобразуется в [[Константы#Константные объекты|константный объект]] класса <tt>int</tt>, который подобно любому другому объекту этого же класса будет иметь свойство <tt>factorial</tt>, которое мы и используем. При попытке чтения из этого свойства будет вызван метод <tt>GetFactorial()</tt>, результат выполнения которого возвращается как значение свойства, то есть как факториал числа. <!--Его мы вызываем с помощью специального синтаксиса, для передачи блока в качестве [[Функции#Аргументы|параметра функции]]. При этом сам блок указывается прямо в коде вызова функции. Тело блока состоит из одного вызова функции, выводящей текущее число в стандартный поток вывода. --><br />
<br />
: Обратите внимание, что функция <tt>puts()</tt>, принимает в качестве параметра [[Стандартные типы данных#Строки|строку]], в то время как тип свойства определен как <tt>[[Стандартные типы данных#Целые числа|int]]</tt>. В этом нет ничего странного, потому что класс <tt>int</tt> имеет [[Типы операторов#Операторы приведения типов|оператор приведения типа]] к классу <tt>[[Стандартные типы данных#Строки|string]]</tt>, который вызывается компилятором автоматически. Таким образом, при вычислении значения выражения в скобках, сперва значение факториала ''приводится'' к типу строки, которая складываясь со строкой слева от оператора <tt>+</tt>, передается в качестве параметра функции.<br />
<br />
<br />
'''Примечание:''' При работе с расширениями существует одно важное обстоятельство. Методы, объявленные в расширении, будут перекрывать соответствующие им методы класса. Важно понимать, что такое перекрытие не является объявлением виртуального метода — происходит именно замещение старого метода новым.<br />
<br />
<!-- '''Примечание:''' При работе с расширениями существует одно важное ограничение. Расширять классы можно только методами и свойствами, но не полями. Это связано с внутренней организацией языка К++ и виртуальной машины. Дополнительно стоит отметить, что методы объявленные в расширении будут перекрывать соответствующие им методы класса (имеется в виду ситуация когда имена и наборы параметров полностью совпадают). Важно понимать, что такое перекрытие не является объявлением виртуального метода — происходит именно замещение старого метода новым. --><br />
<br />
== Смотри также ==</div>Raw mathttp://man.deeptown.org/index.php/%D0%98%D0%B4%D0%B5%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_%D1%8F%D0%B7%D1%8B%D0%BA%D0%B0Идеология языка2013-07-13T11:37:49Z<p>Raw mat: </p>
<hr />
<div>== Немного теории о языках программирования ==<br />
<br />
В этой главе мы кратко рассмотрим основные понятия, касающиеся языков программирования, а также попробуем классифицировать язык К++ с позиции этих понятий.<br />
<br />
Любой язык программирования создается для решения определенного круга задач. Не существует языка, который бы позволял одинаково удобно решать задачи любого характера. Языки программирования различают по многим критериям, делят на большое количество классов и т. д. Мы же рассмотрим такой вопрос как идеологические особенности языка. Здесь, под идеологией языка понимаются некоторые конкретные подходы к отдельным вопросам, например к используемым парадигмам программирования, принципам организации памяти, отношению к исключениям и т. д.<br />
<br />
== Компиляторы и интерпретаторы ==<br />
<br />
Все языки программирования можно условно разделить на два класса: компилируемые и интерпретируемые. <br />
<br />
''Компилируемые языки программирования'' — это такие языки, для обработки которых используются ''компиляторы''. Они преобразуют исходный код программы в машинный код, понятный конкретному процессору и способный выполняться на нем непосредственно. К достоинствам такого подхода можно отнести то, что программа, написанная целиком на машинном коде, имеет высокую производительность. Недостатком является то, что скомпилированная программа ориентирована на конкретную архитектуру и на конкретный процессор. В результате, программа, собранная для одного процессора не будет работать на другом.<br />
<br />
''Интерпретируемые языки программирования'' — языки, для обработки которых используются ''интерпретаторы'', которые (в противовес компиляторам) не производят преобразования программы в машинные коды, а исполняют ее непосредственно. Конечно, производительность интерпретаторов заметно меньше, однако для выполнения, программу не требуется компилировать — она будет работать на любой платформе, где есть реализация ее интерпретатора. <br />
<br />
Условным это разделение было названо потому, что граница между компиляторами и интерпретаторами очень прозрачная. Практически любой компилируемый язык можно интерпретировать, в то же время многие современные интерпретаторы для повышения быстродействия производят т. н. "компиляцию на лету", при которой исходный текст программы преобразуется в абстрактный набор инструкций или байт код, который уже выполняется.<br />
<br />
Язык К++ как раз относится к такой промежуточной категории языков. В результате компилирования исходного текста программы на языке К++ получается байт код модуля, который уже может быть выполнен на виртуальной машине gide. Байт код является платформенно независимым, что позволяет исполнять его на любой платформе, где существует реализация виртуальной машины.<br />
<br />
== Парадигменность ==<br />
<br />
[http://ru.wikipedia.org Википедия] дает следующее определение понятия парадигма программирования: <br />
<br />
«''Парадигма программирования — это [http://ru.wikipedia.org/wiki/Парадигма парадигма], определяющая стиль программирования, иначе говоря — некоторый цельный набор идей и рекомендаций, определяющих стиль написания программ. Парадигма программирования представляет (и определяет) то, как программист видит выполнение программы. Например, в объектно-ориентированном программировании, программист рассматривает программу как набор взаимодействующих объектов, тогда как в функциональном программировании программа представляется в виде цепочки вычисления функций''».<br />
<br />
В общем смысле, под ''парадигмой'' можно понимать некоторый набор концепций, которые применяются при построении программы. Языки, которые позволяют писать программы в рамках нескольких парадигм, называются ''мультипарадигменными''. Например, язык К++ позволяет писать программы в структурном (совокупность функций, вызывающих друг друга), объектно-ориентированном (построение объектной модели) и смешанном (объединение обоих подходов) стилях. Следовательно, К++ тоже может служить примером '''мультипарадигменного''' языка.<br />
<br />
Говоря о парадигмах, нельзя не упомянуть подлинно объектно-ориентированную природу языка К++, которая достигается уже на уровне виртуальной машины. В отличие от C++, где объектная модель является всего лишь "надстройкой" над структурной основой, язык К++, подобно Ruby и его предтечи — языку Smalltalk, придерживается принципа "все есть объект". Это значит, что все элементы языка являются объектами. Встроенные типы данных отсутствуют, вместо них применяются классы из [[Стандартная библиотека gide|стандартной библиотеки]] языка. Это позволяет добиться удивительной гибкости при проектировании программ, вплоть до того, что программист может видоизменять даже стандартные классы, расширяя их и добавляя в них собственный функционал.<br />
<br />
'''Примечание:''' Более подробно, о расширениях можно почитать в кратком [[Введение, или краткий обзор#Расширение классов|обзоре]], либо в главе [[K++ как объектно-ориентированный язык#Расширения|K++ как объектно-ориентированный язык]].<br />
<br />
== Понятие о типизации ==<br />
<br />
Если говорить кратко, то под типизацией понимаются принципы организации переменных в языке. Различают ''статическую типизацию'', при которой каждой переменной ставится в соответствие ее тип, который не может быть изменен в процессе выполнения программы, и ''динамическую типизацию'', где переменные меняют свой тип от случая к случаю. К++ вводит понятие ''полудинамической типизации'', которая сочетает в себе преимущества обоих подходов и позволяет взять лучшие стороны каждого из них.<br />
<br />
'''Примечание:''' Более подробно понятие типизации рассмотрено в главе [[Переменные]].<br />
<br />
== Управление памятью ==<br />
<br />
Различные языки подразумевают разные способы управления памятью. Некоторые языки, например, такие как Си, предоставляют программисту прямой доступ к памяти, при этом программист сам должен следить за тем, чтобы память выделялась по мере обращения к ней и, самое главное, своевременно освобождалась после использования.<br />
<br />
Другие же языки, предоставляют программисту лишь тот уровень абстракции, на который они рассчитаны, при том, что вся работа по управлению памятью ложится на сам язык. Это уберегает программиста от проблем и ошибок, связанных с неправильной работой с памятью. Например, язык Java не предполагает прямого доступа к памяти виртуальной машины. Взамен, он предоставляет наборы классов, которые должны обеспечить программиста всеми необходимыми инструментами.<br />
<br />
Подобно Java, язык К++ так же не предполагает прямого доступа к памяти. В терминах языка программист описывает свои требования к хранилищу данных, не заботясь о том, как нужно выделять память и когда ее освобождать.<br />
<br />
== Понятие исключения ==<br />
<br />
С развитием вычислительной техники и усложнения программ возникла проблема стабильности программ. Традиционный подход, при котором функция возвращает код ошибки, превратил разработку программного обеспечения в настоящий ад, при том, что буквально каждый вызов функции приходилось оборачивать в соответствующее условие проверки кода ошибки. В некоторых областях это все же оправданно (например, ядро Диптауна с успехом применяет этот механизм в совокупности со стеком ошибок). В других областях, таких как сфера прикладного ПО такой подход менее востребован, поскольку там приводятся жесткие требования к скорости написания программ и к их качеству.<br />
<br />
В результате, возникла концепция исключительных ситуаций или ''исключений''. ''Механизм исключений'', — это концепция, позволяющая повысить стабильность работы программы путем внесения в нее своеобразных "контрольных точек". Если в процессе работы программы происходит непредвиденная ситуация, например, такая как ошибка ввода-вывода или внезапное прекращение сеанса связи, то код, обнаруживший ошибку, создает специальный объект-исключение и "выбрасывает" его, то есть ''генерирует исключение''. При этом, выполнение программы прерывается и управление передается ближайшей контрольной точке, которая может перехватить это исключение и принять некоторое решение относительно того что делать дальше: например, показать пользователю сообщение об ошибке.<br />
<br />
Преимущество концепции исключений в том, что программисту не надо проверять каждый вызов на правильность. Он просто пишет код, предполагая что "все идет как надо". Лишь в некоторых местах, где заранее предусматривается возможность возникновения ошибки, вставляются те самые опорные точки — перехватчики исключений. Если же ошибка произойдет, то она рано или поздно будет отловлена.</div>Raw mathttp://man.deeptown.org/index.php/%D0%97%D0%B0%D0%BF%D1%83%D1%81%D0%BA_%D1%84%D0%B0%D0%B9%D0%BB%D0%B0_kpp_%D0%B8%D0%B7_%D0%BA%D0%BE%D0%BD%D1%81%D0%BE%D0%BB%D0%B8_%D0%94%D0%B8%D0%BF%D1%82%D0%B0%D1%83%D0%BD%D0%B0Запуск файла kpp из консоли Диптауна2013-07-13T11:29:31Z<p>Raw mat: </p>
<hr />
<div>Для запуска модуля, написанного на языке K++, нужно сделать следующее:<br />
# Написать сам модуль, сохранить, а затем скомпилировать его в файл формата .gbc.<br />
# Поместить получившийся бинарный файл в одну из директорий, предназначенных для бинарных исполняемых файлов ( к примеру, '''../media/storage/bin''' ).<br />
# Отредактировать файл '''.info''', находящийся в той же директории, поместив в него запись о вновь добавленном модуле, в формате '''''module_name = dddd'''''.<br />
# Стереть все сгенерированные ранее файлы '''.index'''.<br />
# Запустить Диптаун.<br />
Теперь вызов соответствующего модуля возможен простым написанием имени файла, полученного в результате компиляции, в консоли Диптауна.<br />
(''Следует помнить, что нужно использовать то название, которое было присвоено файлу в процессе компиляции.'')</div>Raw mathttp://man.deeptown.org/index.php/%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B8Задачи2013-07-13T10:46:03Z<p>Raw mat: </p>
<hr />
<div>В данном разделе мы публикуем текущий список задач Deeptown SDK. Большая часть задач — это разработка тех или иных модулей/программ на языке [[K++]]. Некоторые задачи предполагают реализацию на C++.<br />
<br />
Данный раздел будет пополняться со временем. Особо важные изменения будут отмечаться в [[Рассылка|рассылке]].<br />
<br />
== Информация ==<br />
<br />
=== Для кого этот раздел ===<br />
<br />
Браться за выполнение задач может абсолютно любой желающий. В том числе и Вы, дорогой читатель ;)<br />
<br />
Для простых задач потребуются только базовые навыки программирования и знание [[K++]], который [[K++|подробно описан]] на этом сайте. Категории сложности задач обозначены в их описаниях.<br />
<br />
Однако будьте готовы к тому, что мы не примем некачественный код. Мы следим за качеством нашей системы. Перед тем, как приступать, пожалуйста, ознакомьтесь с [[Правила оформления кода|правилами оформления кода]].<br />
<br />
=== Оформление данного раздела ===<br />
<br />
В данном разделе задачи разбиты на несколько категорий. Каждая задача — это подраздел в своей категории. Простые задачи описываются прямо в тексте; для более сложных даются ссылки на отдельные страницы описаний. Новые задачи всегда добавляются наверх своего раздела.<br />
<br />
Для каждой задачи приводится следующая служебная информация:<br />
* '''сложность''' — уровень сложности задачи: простая, средняя, сложная<br />
* '''технологии''' — список технологий (языков, библиотек, etc), которые потребуются для выполнения задачи<br />
* '''приоритет''' — степень важности задачи: низкий, средний, высокий<br />
* '''автор''' — подпись автора задачи<br />
* '''примечания''' — технические примечания автора<br />
* '''исполнитель''' — контактная информация исполнителя<br />
* '''срок''' — срок завершения, обозначенный исполнителем<br />
<br />
=== Порядок выполнения задач ===<br />
<br />
Прежде всего, выберите задачу, за которую Вы хотели бы взяться. Выбирать следует только из тех задач, для которых не указан исполнитель. Предпочтительнее брать задачи с более высоким приоритетом, но это не обязательно: прежде всего выбирайте то, что Вам будет интересно делать.<br />
<br />
После того как выбор сделан, вставьте информацию об исполнителе и о сроке.<br />
<br />
В информации об исполнителе должно указываться какое-нибудь средство связи с Вами: e-mail, ICQ или jabber. Вы можете написать свои контактные данные в "мою страницу" вики, а в графу "исполнитель" просто поставить подпись.<br />
<br />
Срок введен для того, чтобы отсекать "призраков". Представьте что кто-то взял задачу и пропал на долгое время. С одной стороны передавать эту задачу кому-то другому нельзя, потому что она уже занята; с другой — работа стоит.<br />
<br />
Срок — это не строгое поле. Если Вы активно работаете над задачей, но не успеваете в срок — Вы всегда можете его отодвинуть. Главное для нас то, что процесс идет.<br />
<br />
В поле "срок" следует вписать предполагаемую дату завершения. Максимальный срок, который Вы можете установить — это текущая дата плюс 2 недели, если иное не указано в примечаниях к задаче.<br />
<br />
Процедура обнаружения "призраков" такова. Когда срок исполнения выходит, мы связываемся с Вами по указанным контактным данным, и узнаем статус задачи/договариваемся о дальнейшем. Если связаться не удается в течение недели — мы убираем информацию об исполнителе, открывая таким образом задачу для других.<br />
<br />
=== Порядок приема задач ===<br />
<br />
На данный момент у нас нет (публичного) централизованного обменника исходными кодами. Поэтому, высылайте свои труды в архиве на адрес developers (гав) deeptown.org. Либо Вы можете выложить их куда-нибудь в интернет, и выслать ссылку по этому адресу.<br />
<br />
=== Условия лицензирования ===<br />
<br />
Мы можем принять Ваш код только в том случае, если Вы передаете его нам на условиях свободной лицензии. Пожалуйста, вложите текст лицензии в архив с кодом. Мы не будем принимать архивы без текстов лицензии, поскольку это может грозить нам судебными исками (исключение — только для наших разработчиков, которые подписали с нами договор).<br />
<br />
Предпочтительной является [http://www.opensource.org/licenses/mit-license.php лицензия MIT], но Вы можете взять другую свободную лицензию или даже написать свою. Однако имейте ввиду: мы ответственно подходим к лицензированию кода, поэтому без внимания мы это не оставим.<br />
<br />
Со своей стороны обещаем соблюдать условия лицензии, либо не принимать Ваш код, если нас она не устроит.<br />
<br />
=== Графические обозначения ===<br />
<br />
Для облегчения навигации по заданиям применено цветовое кодирование сложности поставленной задачи и ее востребованности:<br />
<br />
Категории сложности:<br />
: <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span><br />
: <span style="color: white; background-color: #008000;">· низкая ·</span><br />
: <span style="color: white; background-color: #FFCC00;">· средняя ·</span><br />
: <span style="color: white; background-color: #FF8000;">· выше среднего ·</span><br />
: <span style="color: white; background-color: #DD0000;">· высокая ·</span><br />
<br />
<br />
Категории востребованности (приоритет):<br />
: <span style="color: white; background-color: #A0A0A0;">· может подождать ·</span><br />
: <span style="color: white; background-color: #808080;">· низкий ·</span><br />
: <span style="color: white; background-color: #FFCC00;">· средний ·</span><br />
: <span style="color: white; background-color: #FF8000;">· выше среднего ·</span><br />
: <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
<br />
== Операторы DSH ==<br />
<br />
По сути это команды, но играют роль структурных элементов шеллового языка программирования. Необходимо реализовать команды: true, false, test, if, switch, for, foreach.<br />
<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': <br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 07:38, 18 июня 2008 (EDT)<br />
* '''Примечания''': <br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
== Базовые команды оболочки DSH ==<br />
<br />
Ниже приведены команды, которые требуется реализовать для нормального функционирования командной оболочки Диптауна (DSH). Дабы не изобретать велосипед, было решено делать команды максимально похожими на их UNIX аналоги. Однако, следует помнить, что при реализации команд, надо учитывать специфику диптауна и ориентироваться на максимальное юзабилити а не стопроцентное соответствие UNIX. Короче говоря, не надо стараться реализовать полный клон команды, со всеми "наворотами" — достаточно только базовых возможностей, которые гарантированно понадобятся и будут полезны.<br />
<br />
'''Примечание''': Все без исключения команды должны поддерживать следующие ключи:<br />
;'''--version''': Вывод информации о версии программы и ее авторе.<br />
;'''--help''': Вывод краткой справки по использованию команды. Может выводиться так же при отсутствии какого либо ввода со стороны пользователя (вызов команды без параметров), если это не противоречит логике работы команды.<br />
;'''--''': Индикатор окончания списка параметров. Если в строке параметров встречается данный символ, это означает что дальнейшая информация уже не является параметрами. Например, команды <br />
::<tt>deep$ ls -l</tt><br />
и<br />
::<tt>deep$ ls -- -l</tt><br />
:имеют различный смысл. В первом случае вызывается расширенный список файлов текущей директории, тогда как во втором делается попытка отобразить содержимое директории с именем "-l".<br />
;'''-''': Отдельный дефиз может использоваться всесто имени файла для указания того, что данные требуется получать из стандартного устройства ввода. А вывод — соответственно направлять в устройство вывода. Конкретное поведение зависит от используемой команды. Таким образом, следующие команды являются эквивалентами:<br />
::<tt>deep$ cat </tt><br />
::<tt>deep$ cat - </tt><br />
<br />
<br />
=== <s>ls</s> === <br />
<br />
Команда ls (от англ. ''list'' — список) выводит в терминал содержимое некоторой директории. Формат списка, а так же исследуемая директория, задются с помощью соответствующих ключей. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]], [[регулярные выражения]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 07:38, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=ls здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано<br />
<br />
=== <s>cat</s> === <br />
<br />
Команда cat выводит в терминал содержимое некоторого файла.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]], [[регулярные выражения]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 07:38, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=ls здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано<br />
<br />
<br />
=== <s>cp</s> === <br />
<br />
Команда cp (от англ. ''copy'' — копировать) производит копирование содержимого указанного каталога в новый каталог. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=cp здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': реализовано<br />
<br />
<br />
=== mv === <br />
<br />
Команда mv (от англ. ''move'' — переместить) производит перемещение содержимого указанного каталога в новый каталог, либо переименовывает файлы (каталоги). <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=mv здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== <s>rm</s> === <br />
<br />
Команда rm (от англ. ''remove'' — удалить) удаляет файл, либо каталог. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=rm здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': реализовано<br />
<br />
<br />
=== <s>touch</s> === <br />
<br />
Команда touch (от англ. ''touch'' — потрогать) создает пустой файл с указанным именем, либо изменяет время доступа существующего файла. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=touch здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': реализовано<br />
<br />
<br />
=== mkdir === <br />
<br />
Команда mkdir (от англ. ''make directory'') создает директорию с указанным именем. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=mkdir здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== grep === <br />
<br />
Команда grep производит фильтрацию своего входного потока и выдает результат в выходной поток. Следует иметь в виду, что реализация PCRE (используемая в К++) синтаксически отличается от канонической UNIX. Основная задача — выборка интересующих строк из входного потока.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FF8000;">· выше среднего ·</span><br />
<br />
* '''Технологии''': [[DISS]], [[регулярные выражения]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=grep здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== sed === <br />
<br />
Команда sed (от англ. ''stream editor'' — редактор потоков) производит фильтрацию своего входного потока и выдает результат в выходной поток. Следует иметь в виду, что реализация PCRE (используемая в К++) синтаксически отличается от канонической UNIX. Основная задача — преобразование данных по шаблону.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[DISS]], [[регулярные выражения]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=sed здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== wc === <br />
<br />
Команда wc (от англ. ''word counter'' — счетчик слов) производит подсчет количества структурных элементов в своем входном потоке и выдает результат в выходной поток. В качестве таких элементов могут выступать символы (ключ <tt>-c</tt>), строки (ключ <tt>-l</tt>) и др. Таким образом, простейший способ подсчета количества строк в файле может выглядеть так:<br />
:<tt>deep$ cat myfile | wc -l</tt><br />
<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[DISS]], [[регулярные выражения]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=wc здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== nl === <br />
<br />
Команда nl (от англ. ''numbered lines'' — пронумерованные строки) записывает в стандартный поток вывода данные из своего стандартного потока ввода, предворяя их номерами строк. Может использоваться для формирования листингов программ, составления отчетов и др.<br />
<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': <br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=nl здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== sort === <br />
<br />
Команда sort производит сортировку данных из своего входного потока и выдает результат в выходной поток. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=sort здесь].<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== head === <br />
<br />
Команда head возвращает первые N строк из своего входного потока. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=head здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== tail === <br />
<br />
Команда head возвращает последние N строк из своего входного потока. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=tail здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== <s>more</s> === <br />
<br />
Команда more выводит данные из своего входного потока порциями по N строк. Используется для постепенного отображения содержимого буфера в консоль (так, чтобы пользователь успел все прочитать).<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=more здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано<br />
<br />
<br />
=== mount === <br />
<br />
Команда mount используется для привязки носителей данных к файловой системе Диптауна. Принципы ее работы несколько отличаются от UNIX аналога, поэтому документация не дается. Для получения информации, необходимо [[Обратная связь|связаться с разработчиками]].<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FF8000;">· выше среднего ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''':<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== chmod === <br />
<br />
Команда chmod используется для изменения прав доступа к файлам в файловой системе Диптауна. Принципы ее работы несколько отличаются от UNIX аналога, поэтому документация не дается. Для получения информации, необходимо [[Обратная связь|связаться с разработчиками]].<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FF8000;">· выше среднего ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''':<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== chown === <br />
<br />
Команда chown используется для изменения владельца и группы файлов в файловой системе Диптауна. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FFCC00;">· средний ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=chown здесь].<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== chattr === <br />
<br />
Команда chattr используется для изменения атрибутов файлов (в основном метаинформации) в файловой системе Диптауна. Принципы ее работы несколько отличаются от UNIX аналога, поэтому документация не дается. Для получения информации, необходимо [[Обратная связь|связаться с разработчиками]].<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FFCC00;">· средний ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': <br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== su === <br />
<br />
Команда su (от англ. ''switch user'' — переключить пользователя) используется для запуска подоболочки от имени другого пользователя.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[UserDB]], [[DSH]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=su здесь].<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== sudo === <br />
<br />
Команда sudo позволяет выполнить команду от имени другого пользователя.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[UserDB]], [[DSH]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=sudo здесь].<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== passwd === <br />
<br />
Команда passwd позволяет сменить пароль пользователя.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FFCC00;">· средний ·</span><br />
<br />
* '''Технологии''': [[UserDB]], [[DSH]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=passwd здесь].<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
== Стандартный интерфейс SQL ==<br />
== Универсальный HTTP клиент ==<br />
== Универсальный HTTP сервер ==<br />
== Репозиторий медиаданных ==</div>Raw mathttp://man.deeptown.org/index.php/%D0%92%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8FВыражения2013-07-13T10:45:29Z<p>Raw mat: </p>
<hr />
<div>Как уже было упомянуто в предыдущих главах ([[Классы и объекты#История развития ООП|в истории ООП]]), одним из наиболее значимых нововведений языков программрования высокого уровня, стала возможность записи арифметических выражений в естественной для человека форме, с использованием операторов и функций. Для того чтобы посчитать значение выражения, теперь программисту требуется лишь правильно записать его, не заботясь о том, как оно будет вычисляться. Всю работу по разбору такого выражения, определению очередности выполнения операций берет на себя компилятор языка.<br />
<br />
В этой главе мы узнаем, что такое выражения с точки зрения языка К++, как они формируются и на что нужно обращать внимание при их записи. Кратко будет затронута тема операторов, будут даны основные сведения о них. Более подробно, операторы как элемент языка К++, будут рассмотрены в конце книги, в [[Обзор операторов|отдельном разделе]].<br />
<br />
== Арифметические операции ==<br />
<br />
Наиболее простой и понятный тип выражений — это самые обычные арифметические выражения:<br />
<br />
<source lang="kpp"><br />
var x = 2 + 3 * 4;<br />
var y = (2 + 3) * 4;<br />
</source><br />
<br />
Для простоты мы используем хорошо нам известную конструкцию объявления переменной, но на этот раз в инициализаторе мы указываем не одну константу, вроде строки <tt>"hello world"</tt>, а целое выражение. Выражением считается все, что записано между знаком "<tt>=</tt>" и точкой с запятой "<tt>;</tt>", которая завершает конструкцию.<br />
<br />
При вычислении значения выражения применяются те же самые правила что и в обычной математике, то есть операции имеют ''приоритет'' и выполняются строго в порядке убывания приоритета. В первом случае, сначала будет вычислено ''подвыражение'' <tt>3 * 4</tt>, к значению которого будет добавлено число 2; сумма будет установлена как значение переменной. Во втором случае, приоритет операций был изменен введением круглых скобок, которые имеют тот же смысл, что и в математике: сначала вычисляется значение в скобках, а затем остальные операции, опять же в порядке их приоритета. Скобок в выражение может быть сколько угодно, допускается вложенность.<br />
<br />
...Таким образом, переменной ''x'' будет присвоено значение 14, а переменная ''y'' будет равна 20.<br />
<br />
В выражениях могут указываться не только числовые константы. Так же как и в математике, в выражениях можно использовать переменные. <br />
<br />
При вычислении выражения вмсето имени переменной подставляется ее значение. Таким образом, если в ходе рассчетов выражения, переменная изменит свое значение, то вместе следующего вхождения переменной может быть подставлено уже новое значение. А чтобы с уверенностью сказать "может быть подставлено" или "будет подставлено" нужно смотреть, какой именно код записан в выражении. С этим моментом связана известная проблема неоднозначности выражений. Забежав вперед, можно рассмотреть связанный с данным вопросом пример применения [[Обзор операторов|операторов]] в выражении:<br />
<br />
<source lang="kpp"><br />
var i = 5;<br />
var x = ++i + ++i;<br />
</source><br />
<br />
Как вы думаете, какое значение будет иметь переменная ''x'' после выполнения вышеприведенного кода? Программист, знающий только Паскаль при виде этого примера скорее всего впадет в ступор, поскольку этот язык не имеет оператора <tt>++</tt>, либо он сочтет что выражение записано ошибочно. <br />
<br />
Новичок, изучающий язык C++, но уже кое-что знающий о нем, может рассуждать так: ''«Оператор ++ — это оператор инкремента. В зависимости от того где он расположен в выражении, будет зависеть то как он изменяет переменную; если оператор находится слева он переменной, то сначала будет произведена операция инкремента, а затем новое значение будет использоваться при расчете выражения. В нашем случае это именно так. Стало быть, при рассчете выражения, в обоих случаях значение переменной i будет увеличено на единицу и будет использовано в рассчете. В результате мы получим выражение x = 6 + 7 = 13»''<br />
<br />
Опытный программист на C++, первым делом спросит как реализованы операторы <tt>++</tt> и <tt>+</tt>, и происходит ли копирование аргументов. Если копирование происходит, то значение будет таким же как у новичка, то есть 13. Если же оператор работает с самой переменной, то произойдет следующее:<br />
<br />
Первый оператор <tt>++</tt> увеличит значение переменной ''i'' на единицу, которое теперь будет равно 6. Поскольку мы имеем дело со ссылкой на переменную то это значение не будет нигде сохраняться. Далее, при вычислении значения второго слагаемого сработает уже второй оператор <tt>++</tt>, который так же увеличит (уже увеличенное значение!) переменную на 1, и вернет результат 6 + 1, то есть 7. При вычислении итогового значения мы получаем выражение 7 + 7, то есть 14. <br />
<br />
Получается что сколько человек, столько и мнений. А самое интересное, что каждый из них прав. Их рассуждения логичны с учетом того, с какой позиции смотреть на проблему. В такой неразберихе не мудрено запутаться. Если читатель еще не сбит с толку окончательно, то можно продолжить и рассмотреть эту задачу с точки зрения языка К++.<br />
<br />
В случае с языком К++ вычисление значения будет происходить по второму сценарию. То есть, результат такого выражения будет равен 14.<br />
<br />
== Вызов функций ==<br />
<br />
Вызов [[Функции|функций]] в выражениях осуществляется путем записи имени функции, следом за которым в круглых скобках перечисляются ее [[Функции#Аргументы|аргументы]]. При этом, при вычислении выражения, вместо самой функции будет подставлено ее значение:<br />
<br />
<source lang="kpp"><br />
const pi = 3.1415926;<br />
var x = 10;<br />
var y = x * sin(pi);<br />
</source><br />
<br />
== Доступ к полям ==<br />
<br />
Доступ к [[Классы и объекты#Поля|полям]] и [[Классы и объекты#Свойства|свойствам]] классов осуществляется путем записи имени объекта, следом закоторым ставится оператор точка "<tt>.</tt>", а затем [[идентификатор]] поля либо метода. Если ссылаемое поле само имеет свойства или поля, то к ним так же можно ссылаться, поставив вторую точку и указав идентификатор и т. д. <br />
<br />
<source lang="kpp" line="1"><br />
class MyClass {<br />
var m_Field = "hello world";<br />
property field read m_Field write m_Field;<br />
public function SetField(const string what = "world") {<br />
m_Field = "hello " + what;<br />
}<br />
}<br />
<br />
function caller() {<br />
var object = new MyClass;<br />
puts(object.field);<br />
object.field = "hello everyone!";<br />
object.SetField("universe");<br />
object.SetField();<br />
}<br />
</source><br />
<br />
<br />
;1-7: Мы объявляем некоторый класс <tt>MyClass</tt>, который имеет поле ''m_Field'' и свойство ''field'', связанное с полем. Так же имеется метод <tt>SetField()</tt>, который устанавливает новое значение свойства, которое определяется выражением в строке 5. Значение свойства складывается из строковой константы <tt>"hello "</tt>, к которой добавляется значение параметра ''what''.<br />
<br />
;9-15: В этой функции приводится пример применения вышеописанного класса. Сначала мы создаем инстанцию класса <tt>MyClass</tt> с помощью оператора <tt>'''new'''</tt>. Затем вызывается функция <tt>puts()</tt>, которая печатает значение свойства объекта. В третьей строке мы присваиваем свойству <tt>field</tt> (а значит и полю ''m_Field'') новое значение. Последние две строки показывают пример вызова метода с явным аргументом и с аргументом по умолчанию.<br />
<br />
== Операторы ==<br />
<br />
С точки зрения языка К++, ''оператор'' — это специальная функция, имеющая строго определенное название, принимающая определенный набор параметров и возвращающая определенное значение. Фактически, операторы являются частью самого языка и обрабатываются отдельно от обычных функций еще на этапе синтаксического разбора программы, а следовательно, могут иметь любое имя (то есть, на них не распространяются правила именования [[Идентификатор|идентификаторов]]). Так и происходит. К примеру, все арифметические выражения что мы писали ранее, содержат наборы числовых констант, переменных и операторов, реализующих арифметические операции. То есть, <tt>+</tt>, <tt>-</tt>, <tt>*</tt>, <tt>/</tt>, <tt>++</tt> и многие другие — это операторы. Например, оператор <tt>+</tt>, объявленный в реализации [[Стандартная библиотека Gide|стандартной библиотеки]] для класса <tt>[[Стандартные типы данных#Целые числа|int]]</tt>, принимает в качестве параметра объект того же типа (<tt>int</tt>) и выполняет операцию сложения. <br />
<br />
Существует большое количество разнообразных операторов, которые разделяются на два больших класса: ''унарные'' и ''бинарные'' операторы. Первые работают с единственным объектом (то есть, применяются для него же самого), вторые — оперируют двумя объектами. Таким образом, оператор <tt>+</tt> является бинарным оператором, в то время как <tt>++</tt> является унарным. В языках программирования, операторы применяются для самых различных задач, а не только для арифметики. Например, в книге уже был рассмотрен оператор <tt>'''as'''</tt>, служащий для явного [[Приведение типов|приведения типов]].<br />
<br />
Однако, то что операторы являются частью языка программирования, не означает что программист не может их использовать для собственных нужд в своих классах. Операторы, подобно обычным функциям и методам, позволяют производить ''перегрузку'' и переобъявление. Приведем простой пример, иллюстрирующий этот механизм:<br />
<br />
<source lang="kpp" line="1"><br />
extend MyClass {<br />
public:<br />
operator MyClass += (const MyClass mc) { m_Field += mc.field; return this; }<br />
operator MyClass += (const string s) { m_Field += s; return this; }<br />
const operator string () { return m_Field; }<br />
}<br />
<br />
function f() {<br />
var x = new MyClass;<br />
var y = new MyClass;<br />
x.field = "X";<br />
y.field = "Y";<br />
puts(x += y);<br />
puts(x += "Z");<br />
}<br />
</source><br />
<br />
<br />
;1-6: Используя [[Классы и объекты#Расширения|механизм расширений]], мы дополнили функциональность ранее описанного класса <tt>MyClass</tt>, добавив в него три оператора: две реализации бинарного оператора <tt>+=</tt> и оператор приведения типа. В блоке описания параметров для бинарных операторов мы указываем, с объектами какого типа должна работать данная конкретная реализация оператора. В первом случае, принимается объект нашего же класса <tt>MyClass</tt>, во втором случае — объект стандартного класса <tt>[[Стандартные типы данных#Строки|string]]</tt>. Оба оператора делают одно и то же: добавляют к текущему значению поля ''m_Field'', значение содержащееся в аргументе, то есть, в другом объекте, расположенном справа от оператора. Обратите внимание на тела операторов: для ''конкатенации'' строк применяется тот же самый оператор +=, однако следует понимать, что это уже применяется реализация оператора, объявленная в классе строки. <br />
<br />
: Оператор приведения типов служит для преобразования объекта нашего класса <tt>MyClass</tt> к классу строки. Это может быть необходимо, например, для удобного вывода информации об обекте. При этом, инстанция класса указывается в выражении так, как будто она является строкой. Соответственно, ее можно будет передавать в качестве параметра функциям, принимающим строки — сперва будет выполнена операция приведения типа (с помощью нашего оператора), а затем результат будет передан как параметр. Стоит отметить, что эта операция выполняется компилятором автоматически. От нас требуется только реализовать сам оператор приведения типа.<br />
<br />
;8-12: Для иллюстрирования вышесказанного, мы создаем два экземпляра нашего класса и присваиваем свойству <tt>field</tt> новые значения: "X" и "Y" соответственно.<br />
<br />
;13: Эта строка показывает наш оператор в действии. Как видите, мы выполняем операцию += для двух инстанций нашего класса. При этом, компилятор языка проверит, поддерживает ли наш класс такой оператор для аргумента типа <tt>MyClass</tt>. В нашем случае это так. Следовательно, при вычислении выражения он будет задействован. Результат выполнения операции <tt>x += y</tt> так же является объектом класса <tt>MyClass</tt> (на самом деле, это будет все та же инстанция ''x'', поскольку в реализации оператора применена конструкция "<tt>'''return this''';</tt>"). Но функция <tt>puts()</tt> принимает в качестве параметра строку, значит необходимо выполнить операцию приведения типов. Компилятор языка К++ вторично обратится за помощью к самому классу и проверит наличие оператора приведения к типу <tt>string</tt>. К счастью, такой оператор тоже имеется, поэтому компилятор заключает что типы ''приводимы'', и генерирует вызов оператора приведения. В заключение, результат выполнения оператора приведения (в нашем случае это будет строка "XY") будет передан функции <tt>puts()</tt>, которая уже распечатает его.<br />
<br />
;14: Смысл этой конструкции мало чем отличается от предыдущей, за исключением того, что вторым ''операндом'' указан не объект класса <tt>MyClass</tt>, а строка. Соответственно, при компиляции будет задействована вторая реализация оператора <tt>+=</tt>; в остальном все просиходит точно так же.<br />
<br />
<br />
'''Примечание:''' Здесь мы затронули лишь самые общие сведения об операторах и их применении. На самом деле, это очень обширная тема, требующая отдельного рассмотрения. Более подробно, про операторы можно почитать в [[K++#Операторы|специальном разделе]], целиком посвященном вопросам операторов.</div>Raw mathttp://man.deeptown.org/index.php/%D0%92%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B8%D0%BB%D0%B8_%D0%BA%D1%80%D0%B0%D1%82%D0%BA%D0%B8%D0%B9_%D0%BE%D0%B1%D0%B7%D0%BE%D1%80Введение, или краткий обзор2013-07-13T10:43:15Z<p>Raw mat: </p>
<hr />
<div>__TOC__<br />
<br />
(Введение...)<br />
<br />
== Здравствуй, мир! ==<br />
<br />
Классический пример программы, выводящей строку "Hello, world!" на экран, на языке K++ выглядит следующим образом:<br />
<br />
<source lang="kpp" line="1"><br />
package hello_world;<br />
<br />
export function void main() {<br />
print("Hello, world!\n");<br />
}<br />
</source><br />
<br />
<br />
;1: Как уже [[Модули|отмечалось ранее]], в K++ любая программа или библиотека является модулем. Ключевое слово <tt>'''package'''</tt> используется для объявления имени модуля, в который должна быть скомпилирована программа.<br />
<br />
;3-5: Наконец, объявляется функция <tt>main()</tt>, которая вызывает системную функцию <tt>print()</tt> [[Стандартная библиотека Gide|стандартной библиотеки]], передав ей строку "Hello, world!\n" в качестве параметра. Пара символов <tt>'''\n'''</tt> является специальной последовательностью, которая в момент компиляции преобразуется в символ перевода строки.<br />
<br />
: Ключевое слово <tt>'''export'''</tt> говорит о том, что имя функции не следует декорировать. Декорирование имен применяется для того, чтобы дать возможность [[Функции#Перегрузка функций и операторов|перегружать функции]] с одинаковыми именами, но разными списками параметров (или параметрами разных типов), а так же для реализации [[Пространства имен|пространств имен]].<br />
<br />
<!--<br />
Теперь несколько слов о том, как такая программа будет запущена. В первую очередь стоит отметить, что нет никаких стандартов на имя функции <tt>main()</tt> или объекта ''STDOUT''. На платформе Gide каждая программа является библиотекой, и порядок ее использования полностью определяется пользователем. В данном случае предполагается, что программа будет запускаться при помощи консольной команды '''gide.run''', реализованной в модуле стандартной библиотеки. Эта команда определяет три глобальные переменные ''STDIN'', ''STDOUT'' и ''STDERR'', имеющие тип <tt>console_stream</tt> и запускает функцию <tt>main()</tt>, экспортируемую программой.<br />
<br />
Если бы мы, скажем, писали программу, генерирующую текстуру — никакой функции <tt>main()</tt> в ней не было бы. Набор экспортируемых ей функций полностью определялся бы требованиями генератора текстур, которые описаны в соответствующей документации. --><br />
<br />
== Более сложный пример ==<br />
<br />
Вот более сложный пример, демонстрирующий использование переменных и циклов:<br />
<br />
<source lang="kpp" line="1"><br />
package sums;<br />
<br />
export function void main() {<br />
var ary = [ 1, 2, 3, 4 ];<br />
var sums = new array;<br />
var int i;<br />
sums.push(ary[0]);<br />
for (i = 1; i < ary.size(); ++i)<br />
sums.push(sums[i-1] + ary[i]);<br />
}<br />
</source><br />
<br />
Смысл этой программы понятен интуитивно: создается два массива — исходный (''ary'') и результирующий (''sums''), и затем массив ''sums'' заполняется суммами чисел из массива ''ary'' от 0 до текущего индекса i.<br />
<br />
;4-5: Особое внимание в этом примере следует уделить объявлениям переменных. Мы видим, что объявляются два [[Стандартные типы данных#Массивы|массива]] — ''ary'' и ''sums'', причем в инструкции <tt>'''var'''</tt> их тип никак не указывается. В этом случае работает следующее правило: если при объявлении переменной ее тип не указан, то типом переменной становится тип результата инициализатора переменной, т.е. выражения, стоящего справа от оператора '<tt>=</tt>'.<br />
<br />
:Выражения, записанные в квадратных скобках через запятую — это встроенная в K++ конструкция, создающая объект, имеющий тип <tt>[[Стандартные типы данных#Массивы|array]]</tt> (объявленный в [[Стандартная библиотека gide|стандартной библиотеке]]) и заполненный соответствующими значениями.<br />
<br />
:Оператор <tt>'''new'''</tt> создает экземпляр произвольного класса — здесь мог бы стоять, в том числе, и пользовательский класс. В данном случае <tt>'''new'''</tt> создает экземпляр класса <tt>[[Стандартные типы данных#Массивы|array]]</tt>.<br />
<br />
;6: Здесь мы создаем переменную типа <tt>[[Стандартные типы данных#Целые числа|int]]</tt>. Когда тип указан в самой конструкции <tt>'''var'''</tt> — объект соответствующего класса создается автоматически. Таким образом, например, следующие объявления эквивалентны:<br />
<br />
<source lang="kpp"><br />
var sums = new array;<br />
var sums = [ ];<br />
var array sums;<br />
</source><br />
<br />
или<br />
<br />
<source lang="kpp"><br />
var i = 0;<br />
var i = new int;<br />
var int i;<br />
</source><br />
<br />
Метод <tt>push()</tt> класса <tt>array</tt> добавляет элемент в конец массива; <tt>size()</tt> — возвращает количество элементов массива. Дальнейший код, думаю, объяснять не стоит.<br />
<br />
== Использование блоков ==<br />
<br />
Подобно языку Ruby, в K++ реализован механизм передачи в качестве параметра функции некоторого связанного (с вызовом) блока кода. Функция затем может вызвать этот блок один или более раз, и воспользоваться результатом, который он возвращает.<br />
<br />
Выглядит это следующим образом:<br />
<br />
<source lang="kpp" line="1"><br />
package block_demo;<br />
<br />
function void times(int i, block b) {<br />
for (var n = 0; n < i; n++)<br />
b(n);<br />
}<br />
<br />
export function void main() {<br />
times(3) { |i| print((i as string) + " "); };<br />
print("\n");<br />
}<br />
</source><br />
<br />
Результат выполнения программы:<br />
<br />
0 1 2<br />
<br />
Данный пример также демонстрирует объявление параметров функции: это делается в стиле языка C. Впрочем, тип параметра функции можно опустить — в этом случае будет сгенерирован [[Переменные#Динамическая типизация на примере Ruby|динамический код]].<br />
<br />
;4: Обратите внимание на то, что управляющая переменная создается непосредственно по месту использования, то есть в [[Основные синтаксические конструкции#Цикл_for|цикле <tt>'''for'''</tt>]]. Такой подход является предпочтительным по сравнению с подходом, при котором управляющая переменная создается за пределами цикла. Почему это так, будет сказано в отдельном разделе, посвященном циклам.<br />
<br />
;10:В функции <tt>main()</tt> используется специальный синтаксис для вызова функции <tt>times()</tt>: тело блока идет сразу после оператора вызова функции. Такой синтаксис работает только в том случае, если параметр—блок объявлен последним.<br />
<br />
Можно было бы переписать эту конструкцию так:<br />
<br />
<source lang="kpp"><br />
var b = { |i| print((i as string) + " "); };<br />
times(3,b);<br />
</source><br />
<br />
Параметры блока могут быть перечислены между символами "|" через запятую. В данном случае блок принимает один параметр — номер итерации.<br />
<br />
== Расширение классов ==<br />
<br />
Приведенный выше пример можно переписать следующим образом:<br />
<br />
<source lang="kpp" line="1"><br />
package extend_demo;<br />
<br />
extend int {<br />
public const function void times(block b) {<br />
for (var n = 0; n < this; n++)<br />
b(n);<br />
}<br />
}<br />
<br />
export function void main() {<br />
3.times() { |i| print((i as string) + " "); };<br />
print("\n");<br />
}<br />
</source><br />
<br />
;3-8: Оператор <tt>'''extend'''</tt> расширяет функционал класса — в данном случае класса <tt>[[Стандартные типы данных#Целые числа|int]]</tt>. Его использование аналогично объявлению класса, но объявленные внутри поля, методы и операторы будут добавлены к уже существующему классу.<br />
<br />
: Таким образом, к классу <tt>int</tt>, объявленному в стандартной библиотеке, добавляется метод <tt>times()</tt>, вызывающий связанный блок количество раз, равное текущему числу (не забываем: мы добавляем метод в класс <tt>int</tt>, который отвечает за хранение числа).<br />
<br />
;11: После такого объявления, в функции <tt>main()</tt> мы используем уже метод <tt>times()</tt> класса <tt>int</tt>: конструкция <tt>3.times()</tt> означает создание объекта 3 класса <tt>int</tt> и вызов у этого объекта метода <tt>times()</tt>.<br />
<br />
<br />
'''Примечание:''' Расширение будет работать во всем модуле, а так же во всех модулях, которые импортируют данный модуль.</div>Raw mathttp://man.deeptown.org/index.php/%D0%91%D0%B0%D0%B7%D0%BE%D0%B2%D1%8B%D0%B5_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2%D0%B0_%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D0%B0Базовые свойства контрола2013-07-13T10:41:59Z<p>Raw mat: </p>
<hr />
<div>== Общие свойства ==<br />
<br />
=== Стили ===<br />
; margin : size<br />
; proportion : integer<br />
; expand : {full|shaped|none}<br />
; fixedMinSize : boolean<br />
; align : {left|right|center}<br />
; verticalAlign : {top|middle|bottom}<br />
<br />
== Видимые элементы ==<br />
<br />
=== Стили ===<br />
<br />
; border : {simple|sunken|raised|none|default}<br />
; tabTraversal : boolean<br />
; scroll : {vertical|horizontal|both|none|default}<br />
; enabled : boolean<br />
; visible : boolean<br />
; color : color<br />
; backgroundColor : color<br />
; left : size<br />
; top : size<br />
; width : size<br />
; height : size<br />
; cursor : {pointer|wait|busy|hand|question|none|default}<br />
; fontFamily : string (''generic fonts: default, serif, sans-serif, cursive, fantasy, monospace'') or array of strings<br />
; fontSize : size<br />
; fontWeight : {normal|bold|default}<br />
; fontDecoration : {none|underline|default}<br />
; fontStyle : {normal|italic|default}<br />
; label : string<br />
; minSize : [ size, size ]<br />
; maxSize : [ size, size ]</div>Raw mathttp://man.deeptown.org/index.php/%D0%91%D0%BB%D0%BE%D0%BA%D0%B8Блоки2013-07-13T10:40:38Z<p>Raw mat: </p>
<hr />
<div>__TOC__<br />
<br />
Основной идеей при написании как процедурных, так и объектно-ориентированных программ является локализация (заключение) функционала в некоторой области: в функции, либо в методе. Таким образом получается, что программа состоит из множества функционально независимых частей, каждая из которых решает конкретную, достаточно узкую задачу. Такой подход позволяет скрыть детали реализации алгоритмов внутри этих частей, а "наружу" предоставить только интерфейс — совокупность способов взаимодействия данного функционального элемента с другими. В случае функции, под интерфейсом понимается совокупность ее параметров и возвращаемого значения; в случае классов — наборы методов и свойств.<br />
<br />
Случается, что в работе некоторого алгоритма возникают четко определенные функциональные подсистемы, которые, однако, слишком зависимы от самого алгоритма, либо слишком просты для того, чтобы выделять их в отдельную функцию. В случае с C++ это все же приходится делать, даже если эта "минифункция" применяется всего в одном методе, но достаточное количество раз, чтобы программист не захотел копировать эти строки.<br />
<br />
В других случаях, может возникнуть желание вынести некоторый функционал за пределы текущей функции, или даже предоставить программисту-пользователю возможность самому определить его.<br />
<br />
И в том и в другом случае на помощь приходят ''замыкания'', ''анонимные функции'' или ''лямбда функции'', как их еще называют. Сущность такой функции в том, что она с одной стороны как бы является переменной, а с другой стороны — функцией, которую можно вызывать. В языке К++, такие функции носят название блоков. Сущность переменной, блоки проявляют в том что их можно создавать как локальные переменные, присваивать друг другу и даже передавать другим функциям в качестве параметра. Приняв такой параметр, функция может обратиться к нему, как к любой другой функции: вызывать с некоторыми параметрами и использовать его возвращаемое значение:<br />
<source lang="kpp"><br />
function Caller(x, y, block b) { <br />
return b(x, y);<br />
}<br />
</source><br />
<br />
Приведем простой пример объявления блоков и их передачи в вышеописанную функцию:<br />
<source lang="kpp"><br />
var sum = { |x, y| return x + y; };<br />
var mul = { |x, y| return x * y; };<br />
var s = Caller(2, 3, sum); // s = 2 + 3 = 5<br />
var m = Caller(2, 5, mul); // m = 2 * 5 = 10<br />
</source><br />
<br />
Первый блок принимает два параметра и возвращает их сумму; второй блок так же принимает два параметра, но результатом его выполнения будет уже произведение.<br />
<br />
== Применение блоков ==<br />
<br />
Наиболее подходящая задача, иллюстрирующая идеологию и способы использования блоков — это сортировка элементов массива. Предположим, что программисту требуется реализовать для класса <tt>array</tt> механизм сортировки элементов. Под сортировкой, в данном случае понимается упорядочение набора элементов на основании некоторого критерия, по которому можно судить об их очередности в списке для каждой пары элементов.<br />
<br />
Проблема заключается в том, что на этапе написания реализации алгоритма, мы не имеем представления о том, какие же элементы будут храниться в массиве. Как мы отсортируем массив, который может состоять из чего угодно: строк, чисел, произвольных объектов и даже самих массивов? Пришлось бы либо писать обширную реализацию некоторого метода сравнения двух элементов, который бы учитывал все возможные варианты типов элементов, либо реализовывать свою функцию сортировки на каждый из типов данных элементов:<br />
<source lang="kpp"><br />
extend array {<br />
public function array SortAsNumbers();<br />
public function array SortAsStrings();<br />
public function array SortAsArrays();<br />
public function array SortAsCollections();<br />
}<br />
</source><br />
<br />
И тот и другой способ жестко ограничивает функциональность нашего класса только теми типами элементов, которые были изначально предусмотрены. А если программист-пользователь захочет использовать свой тип данных, или еще проще, захочет сортировать элементы не по возрастанию, а по убыванию? Вероятно, мало кто захочет переписывать все эти функции только для того, чтобы изменить порядок сортировки.<br />
<br />
Давайте взглянем на проблему шире. В сущности, все вышеописанные методы делают ровно одно и то же: некоторым из алгоритмов сортировки, они упорядочивают элементы в массиве. При этом, единственная различающаяся в них часть — это сама операция сравнения двух элементов. Решение напрашивается само собой — необходимо вынести эту часть из функции сортировки и предоставить пользователю самому обеспечить операцию сравнения. Таким образом, мы получим ОДИН метод сортировки, который будет работать для ЛЮБЫХ типов элементов — программист-пользователь сам укажет необходимый критерий сортировки, который уже будет использован внутри метода.<br />
<br />
Эту задачу можно красиво решить с помощью блоков:<br />
<source lang="kpp"><br />
extend array {<br />
public const function array sort(block cmp) {<br />
return this if size < 2;<br />
var array left, array right;<br />
for (var i = 1; i < size; ++i)<br />
cmp(this[i], this[0]) ? left.push(this[i]) : right.push(this[i]);<br />
return left.sort(cmp) + [ this[0] ] + right.sort(cmp);<br />
}<br />
}<br />
</source><br />
<br />
В качестве параметра, метод <tt>sort()</tt> принимает один аргумент — блок, который должен производить сравнение двух элементов и возвращать истину, если элементы расположены в правильном порядке и ложь, если порядок неверный, то есть необхдимо их переставить.<br />
<br />
'''Примечание:''' Приведенный здесь код является реализацией алгоритма [http://ru.wikipedia.org/wiki/Быстрая_сортировка быстрой сортировки], однако он является не очень практичным, поскольку на каждом из этапов он оперирует с копиями соответствующих частей массива; в книге же был использован из соображений краткости кода.<br />
<br />
Теперь, любой массив в программе, использующей текущий модуль, может быть отсортирован на основании некоторого критерия. Результатом выполнения будет другой, уже отсортированный массив:<br />
<br />
<source lang="kpp"><br />
var array sorted, ary = [2, 7, 3, 0, 1, 5, 8, 9, 4, 6];<br />
sorted = ary.sort() { |x, y| x < y; }; // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]<br />
sorted = ary.sort() { |x, y| x > y; }; // [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]<br />
<br />
ary = ['q', 'e', 'b', 'z', 'a', 'x', 't'];<br />
sorted = ary.sort() { |x, y| x < y; }; // ['a', 'b', 'e', 'q', 't', 'x', 'z']<br />
sorted = ary.sort() { |x, y| x > y; }; // ['z', 'x', 't', 'q', 'e', 'b', 'a']<br />
</source><br />
<br />
Мы объявляем две переменных-массива ''ary'' и ''sorted''. Переменная ''ary'' инициализируется массивом произвольных чисел, либо строк во втором случае. Затем выполняются вызовы метода <tt>sort()</tt>, результат выполнения которых сохраняется в переменной ''sorted''. <br />
<br />
'''Примечание:''' Для сокращения записи, в вышеописанном коде были применены т. н. ''встроенные блоки'' (inline). К++ поддерживает специальный синтаксис для передачи встроенных блоков в функции. Подробнее об этом будет написано чуть ниже.<br />
<br />
== Отличие от функций ==<br />
<br />
В целом, блоки очень похожи на обычные функции, однако существует несколько отличий, которые следует иметь в виду. Первое, наиболее очевидное отличие заключается в способе описания формальных параметров, которые, в отличие от функций, записываются в самом начале тела блока, между двумя вертикальными чертами. Друг от друга параметры отделяются запятой. <br />
<br />
Главное отличие блоков заключается в том, что они в основном ориентированы на динамический код. Скажем, возвращаемое значение всегда является [[Переменные#Нетипированные (динамические) переменные|динамической переменной]]. В отдельных случаях допускается типизация аргументов блока, однако она происходит внутри самого блока и не влияет на фактически передаваемые параметры (см. ниже). Таким образом, с точки зрения вызывающего кода, блоки всегда представляют собой динамический код.<br />
<br />
Блоки не могут быть экспортированы из текущего модуля и должны применяться только в нем самом. Разумеется, экспортировать функции, принимающие блоки в качестве параметров можно.<br />
<br />
== Типизация параметров ==<br />
<br />
В некоторых случаях, для повышения эффективности кода блока можно типировать формальные параметры, с тем чтобы впоследствии компилятор, на основании этой информации, мог бы генерировать статический код. Типирование аргументов осуществляется точно так же, как и в обычных функциях: непосредственно перед идентификатором аргумента, указывается его тип.<br />
<source lang="kpp"><br />
var myblock = { | int x, int y | /* код блока */ };<br />
</source><br />
Теперь, в теле блока, переменные ''x'' и ''y'' будут считаться статическими. <br />
<br />
'''Примечание 1:''' Поскольку на момент компиляции, у компилятора нет возможности проверить правильность типов передаваемых в блок параметров, ответственность за это ложится на самого программиста. Если в блок будет передан объект неприводимого типа, то это приведет к ошибке времени исполнения ([[Идеология языка#Понятие исключения|исключению]]).<br />
<br />
'''Примечание 2:''' Указание типов в параметрах может быть удобно, поскольку компилятор, располагая информацией о типе параметров, может выполнять некоторую работу автоматически. Например, приводить переменные к нужному типу при вызове других функций. Допустим, мы хотим описать некоторый блок, который по логике работы принимает число (переменную типа <tt>int</tt>), но внутри себя вызывает другие функции, скажем <tt>puts()</tt>:<br />
<br />
<source lang="kpp" line="1"><br />
var myblock = { |x| <br />
/* код блока */ <br />
puts(x as string); <br />
};<br />
</source><br />
<br />
В данном случае, в строке 3, при вызове <tt>puts()</tt>, нам пришлось привести тип переменной ''x'' к строке явным образом. Если бы этого не было сделано, то возникла бы ошибка времени исполнения, поскольку функция <tt>puts()</tt> ожидает строку. При небольших размерах кода это не составляет проблем, однако приводить тип переменной пришлось бы в каждом вызове. Для решения этой проблемы как раз рекомендуется применять типирование аргументов:<br />
<br />
<source lang="kpp" line="1"><br />
var myblock = { | int x | <br />
/* код блока */ <br />
puts(x); //теперь, приведение происходит автоматически<br />
};<br />
</source><br />
<br />
== Встроенные блоки ==<br />
<br />
Чаще всего, блоки применяются для записи небольших кусочков кода, которые, как в примерах выше, передаются в функции, для уточнения некоторых моментов. Для того чтобы облегчить работу программиста и сделать код более читаемым, был введен специальный синтаксис для передачи блоков в функции с одновременным их объявлением "на месте"; при этом, блоки записываются в сокращенной форме сразу за кодом вызова функции, после блока фактических параметров.<br />
<br />
Покажем использование встроенных блоков на примере нахождения суммы чисел от 1 до 10, а так же получения списка четных чисел в этом же интервале.<br />
<source lang="kpp"><br />
var s = 0;<br />
var numbers = [];<br />
(1..10).each() { |x| s += x; numbers.push(x); };<br />
var evens = numbers.collect() { |x| x % 2 == 0 ? x : null; }.compact();<br />
</source><br />
<br />
Несмотря на свою простоту и надуманность, данный пример показывает две особенности встроенных блоков, по сравнению с обычными блоками, объявляемыми как переменные. Первая особенность заключается в том, что встроенные блоки имеют доступ к локальным переменным в контексте вызова (то есть, замыкаются на него — отсюда и название "замыкания"). Например, данный блок использует внешние по отношению к нему переменные: ''s'' для накопления суммы, а ''numbers'' для добавления текущего числа в массив. <br />
<br />
Вторая особенность реализуется в коде второго встроенного блока. Она заключается в том, что встроенные блоки могут возвращать значения без явного указания оператора <tt>'''return'''</tt>. Однако, это вожможно только для простых выражений, без применения операторов ветвления. Для получения массива целых чисел, используются два метода, объявленные в стандартной библиотеке языка K++: методы <tt>collect()</tt> и <tt>compact()</tt>. <br />
<br />
Метод <tt>collect()</tt> принимает в качестве аргумента блок. Далее, он проходит вдоль всего массива, вызывая блок с параметром текущего элемента. Результат выполнения блока помещается в другой массив, который возвращается вызывающему коду. Таким образом, этот метод может применяться для обработки элементов некоторого массива с получением другого массива из обработанных элементов. Метод <tt>compact()</tt> используется для "уплотнения" массива, путем удаления из него пустых элементов, равных <tt>'''null'''</tt>. <br />
<br />
В вышеприведенном коде, мы используем оба этих метода для того, чтобы сначала выбрать из исходного массива все четные элементы (вместо нечетных вставляется <tt>'''null'''</tt>), а затем уплотнить полученный массив.<br />
<br />
Реализованы эти методы могут быть примерно так:<br />
<source lang="kpp"><br />
extend array {<br />
public const function array collect(block b) {<br />
var array result;<br />
this.each() { |x| result.push(b(x)); };<br />
return result;<br />
}<br />
<br />
public const function array compact() {<br />
var array result;<br />
this.each() { |x| result.push(b(x)) if x; };<br />
return result;<br />
}<br />
}<br />
</source><br />
<br />
'''Примечание 1:''' при работе со встроенными блоками внутри обрабатывающих функций, следует быть очень осторожным. Поскольку, встроенные блоки оперируют с перменными контекста своего объявления, то они должны существовать не дольше чем существует сам блок, в котором они были объявлены и в котором был произведен вызов функции-обработчика. <br />
<br />
Сама функция, должна работать с ними "как есть"; ни в коем случае нельзя их сохранять во внешних переменных (где они могут просуществовать дольше положенного времени). Например, такой код '''является недопустимым''':<br />
<source lang="kpp"><br />
var block_array = [];<br />
function AddBlock(block b) { block_array.push(b); }<br />
function f() {<br />
const c = 5;<br />
AddBlock() { |x| x * c; }; // так делать нельзя!<br />
}<br />
</source><br />
В коде блока мы видим использование внешнего объекта — константы ''c''. Проблема заключается в том, что встроенные блоки не порождают собственного контекста и заимствуют набор локальных переменных из контекста вызова. Соответственно, если блок был где то сохранен, то может случиться, что контект его локальных переменных был уже удален. <br />
<br />
В то же время, такой код вполне правомерен:<br />
<source lang="kpp"><br />
function f() {<br />
var b = { |x| return x + 1; }; // а так можно<br />
AddBlock(b); <br />
<br />
//так тоже можно, но не рекомендуется:<br />
AddBlock({|x| return x * 5;}); <br />
}<br />
</source><br />
<br />
'''Примечание 2:''' хотя использование связки <tt>collect(){...}.compact()</tt> не возбраняется, следует иметь в виду, что происходит двойное копирование массивов, что в конечном счете сказывается на производительности. Для задачи выбора (а не обработки) элементов из массива, более приемлемым вариантом является использование метода <tt>select()</tt>, который отбирает из исходного массива все элементы, удовлетворяющие условию, записанному в блоке:<br />
<br />
<source lang="kpp"><br />
var ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];<br />
var selected = ary.select() { |x| x > 6; }; // [7, 8, 9, 10]<br />
</source><br />
<br />
При использовании метода <tt>select()</tt> промежуточного копирования не происходит, так что операция будет выполняться более эффективно.<br />
<br />
----<br />
<br />
<br />
Механизм встроенных блоков — это очень мощный инструмент, который при грамотном использовании позволяет добиться потрясающих результатов при написании программ. Программы получаются очень лаконичными, легко читаемыми и иногда даже более эффективными, чем при традиционном подходе. <br />
<br />
К примеру, вот пример кода, который обращается на FTP сервер и выбирает из некоторой директории все файлы, имеющие расширение '.conf':<br />
<source lang="kpp"><br />
var configs = Directory.open('ftp://example.com/media/etc')<br />
.browse().select() { |file| file.name =~ `\.conf$`; };<br />
</source><br />
<br />
На традиционных языках, такая операция могла бы занять с десяток строк кода, тогда как здесь мы обходимся всего одной (строка разбита две части для того, чтобы уместить в ширину страницы).<br />
<br />
Учитывая принципы полудинамической типизации, некоторые конструкции могут быть более лаконичными даже по сравнению с подобными языками. Например, следующий код на C# 2.0 позволяет увеличить все элементы массива на 2:<br />
<source lang="csharp"><br />
int[] ary = { 1, 2, 3 };<br />
int x = 2;<br />
Array.ConvertAll<int, int>(ary, delegate(int elem) { return elem * x; }); // { 2, 4, 6 }<br />
</source><br />
<br />
На K++ эта же конструкция будет выглядеть так:<br />
<source lang="kpp"><br />
var ary = [1, 2, 3];<br />
var x = 2;<br />
ary.convert() { |e| e * x; }; // [2, 4, 6]<br />
</source><br />
<br />
Конечно, можно привести массу доводов в защиту того или иного языка, укзазать преимущества однго и недостатки другого, чем с упоением и занимаются многочисленные разжигатели религиозных войн в среде IT. Мы не ставим себе целью доказать преимущества K++, скорее показать преимущества всех языков, использующих альтернативный подход к программированию в целом. К чести того же C# можно сказать, что в третей версии были введены замыкания, по краткости не уступающие K++. В целом, у каждого языка есть свои преимущества и недостатки и в общем случае сравнивать их нельзя. Можно, разве что, судить о степени удобства языка, в сочетании с его производительностью и о том, насколько эффективно он позволяет решать задачи, на которые был рассчитан и исходя из которых проектировался.</div>Raw mathttp://man.deeptown.org/index.php/World_EngineWorld Engine2013-07-13T10:39:25Z<p>Raw mat: </p>
<hr />
<div>World Engine - это движок, в котором реализовано все необходимое для функционирования Диптауна как среды виртуальной реальности. Этот движок включает в себя подсистемы физики, графики, ввода и пользовательского интерфейса.<br />
<br />
В настоящий момент этот движок находится в стадии разработки. Данный раздел документации посвящен описанию этого движка с точки зрения пользователя: здесь описаны форматы файлов конфигурации и данных, а также интерфейсы для написания скриптов на гайд-совместимых языках (например, [[K++]]). Все описания приводятся по состоянию на текущий момент разработки. К первому релизу многое может поменяться, но основные принципы останутся неизменными.<br />
<br />
Данный раздел полезен тем, кто хочет уже сейчас, до выхода релиза Диптауна, начать разбираться и работать с движком. В первую очередь это команда WDK, а также дизайнеры.<br />
<br />
Разделы пока никак не будут систематизированы - такая работа будет проведена ближе к релизу. Советую читать по порядку.<br />
<br />
== Разделы ==<br />
<br />
# [[Понятие объекта в пространстве]]<br />
# [[Механизмы взаимодействия объектов с окружающей средой]]<br />
# [[Основы программирования объектов]]<br />
# [[Описание подсистемы ввода]]<br />
# [[Программирование поведения объекта]]<br />
# [[Алгоритм синхронизации]]</div>Raw mathttp://man.deeptown.org/index.php/StringString2013-07-13T10:38:07Z<p>Raw mat: </p>
<hr />
<div>Класс <tt>string</tt> используется для хранения и обработки стороковых данных. Класс предоставляет большое количество методов для управления строками, таких как: выделение, поиск и замена подстрок. Важно отметить, что все строки хранятся и обрабатываются в кодировке '''UTF-8'''. Например, метод <tt>[[#length|length]]</tt> для этого класса, возвращает длинну строки <u>в символах</u>, а не в байтах. Таким образом, реальный размер строки, содержащей многобайтные символы, не будет совпадать с ее длиной. При необходимости работы со строками как с потоком байтов, следует использовать класс <tt>[[bytea]]</tt>.<br />
<br />
Стандартные библиотеки конкретных языков расширяют функциональность данного класса, вводя дополнительные методы. Например, [[K++#Системная библиотека|библиотека языка K++]] вводит операторы <tt>[]</tt> и <tt>[]=</tt> для упрощения работы со строками. Дополнительно, там же вводятся операции с использованием [[регулярных выражений]].<br />
<br />
<br />
'''Родители''': <tt>[[Object]]</tt> <br><br />
'''Методы''': <tt><br />
[[#getc|getc]] <br />
[[#get|get]] <br />
[[#setc|setc]] <br />
[[#set|set]] <br />
[[#at|at]] <br />
[[#empty|empty]] <br />
[[#length|length]] <br />
[[#clear|clear]] <br />
[[#substr|substr]] <br />
[[#substr_int|substr_int]] <br />
[[#copy|copy]] <br />
[[#copy_int|copy_int]] <br />
[[#replace|replace]] <br />
[[#replace_int|replace_int]] <br />
[[#replace_rel_|replace_rel]] <br />
[[#replace_rel_int|replace_rel_int]] <br />
[[#replace_all|replace_all]] <br />
[[#insert|insert]] <br />
[[#insert_int|insert_int]] <br />
[[#erase|erase]] <br />
[[#erase_int|erase_int]] <br />
[[#find|find]] <br />
[[#find_first_of|find_first_of]] <br />
[[#find_first_not_of|find_first_not_of]] <br />
[[#begin|begin]] <br />
[[#end|end]]</tt><br><br />
'''Приводится к типам''': <tt>[[int]] [[real]] [[uid]] [[bytea]]</tt> <br><br />
'''Реализует операторы''': <br />
<tt>[[#оператор +|+]]</tt>,<br />
<tt>[[#оператор +=|+=]]</tt>, <br />
<tt>[[#оператор <<|<<]]</tt>, <br />
[[оператор присваивания|присваивания]],<br />
[[операторы сравнения|сравнения]] <br><br />
<br />
__NOTOC__<br />
<br />
== оператор + ==<br />
<br />
operator + (''строка'': <tt>string</tt>) <br><br />
'''Возвращает''': <tt>string</tt> <br><br />
<br />
Метод возвращает новую строку, полученную путем конкатенации (сложения) текущей и переданной строк.<br />
<br />
== оператор += ==<br />
<br />
operator += (''строка'': <tt>string</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод изменяет текущую строку, дописывая ей в конец строку, переданную в качестве параметра.<br />
<br />
== оператор << ==<br />
<br />
Эквивалент оператора <tt>[[#оператор +|+]]</tt>.<br />
<br />
== getc ==<br />
<br />
getc(''позиция'': <tt>[[string_iterator]]</tt>) <br><br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает числовой код символа, на который указывает переданный итератор.<br />
<br />
== get ==<br />
<br />
get(''индекс'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает числовой код символа, расположенного по переданному индексу (смещение относительно начала строки). При передаче индекса, выходящего за границы строки, будет возбуждено исключение <tt>[[Классы исключений#ERangeError|ERangeError]]</tt>.<br />
<br />
== setc ==<br />
<br />
setc(''позиция'': <tt>[[string_iterator]]</tt>, ''новый код'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод устанавливает числовой код символа, на который указывает переданный итератор.<br />
<br />
== set ==<br />
<br />
set(''индекс'': <tt>[[int]]</tt>, ''новый код'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод устанавливает числовой код символа, расположенного по переданному индексу (смещение относительно начала строки). При передаче индекса, выходящего за границы строки, будет возбуждено исключение <tt>[[Классы исключений#ERangeError|ERangeError]]</tt>.<br />
<br />
== at ==<br />
<br />
set(''индекс'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': <tt>[[string_iterator]]</tt> <br><br />
<br />
Метод создает строковый итератор и инициализирует его так, чтобы он указывал на символ по переданному индексу.<br />
<br />
== empty ==<br />
<br />
'''Возвращает''': [[логическое значение]] <br><br />
<br />
Метод проверяет строку на наличие в ней данных. <br />
<br />
== length ==<br />
<br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает длину строки в символах. <br />
<br />
== clear ==<br />
<br />
'''Возвращает''': текущий объект <br><br />
<br />
Очищает строку от содержимого. После этого строка считается пустой.<br />
<br />
== substr ==<br />
<br />
substr(''начальная позиция:'' <tt>[[string_iterator]]</tt>, ''количество символов'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': <tt>string</tt> <br><br />
<br />
Метод возвращает указанное количество символов исходной строки, начиная с позиции итератора.<br />
<br />
== substr_int ==<br />
<br />
substr_int(''начальная позиция:'' <tt>[[int]]</tt>, ''количество символов'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': <tt>string</tt> <br><br />
<br />
Метод аналогичен методу <tt>[[#substr|substr]]</tt>, только начальная позиция принимается в виде числа.<br />
<br />
== copy ==<br />
<br />
substr(''начальная позиция:'' <tt>[[string_iterator]]</tt>, ''конечная позиция'': <tt>[[string_iterator]]</tt>) <br><br />
'''Возвращает''': <tt>string</tt> <br><br />
<br />
Метод возвращает подстроку исходной строки по указанным позициям.<br />
<br />
== copy_int ==<br />
<br />
copy_int(''начальная позиция:'' <tt>[[int]]</tt>, ''конечная позиция'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': <tt>string</tt> <br><br />
<br />
Метод аналогичен методу <tt>[[#copy|copy]]</tt>, только позиции принимаются в виде чисел.<br />
<br />
== replace ==<br />
<br />
replace(''начальная позиция'': <tt>[[string_iterator]]</tt>, ''количество символов'': <tt>[[int]]</tt>, ''строка замены'': <tt>string</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод заменяет подстроку исходной строки на данные, предоставленные пользователем.<br />
<br />
== replace_int ==<br />
<br />
replace(''начальная позиция'': <tt>[[int]]</tt>, ''количество символов'': <tt>[[int]]</tt>, ''строка замены'': <tt>string</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод аналогичен методу <tt>[[#replace|replace]]</tt>, только позиция передается в виде числа.<br />
<br />
== replace_rel ==<br />
<br />
== replace_rel_int ==<br />
<br />
== replace_all ==<br />
<br />
replace_all(''подстрока поиска'': <tt>string</tt>, ''подстрока замены'': <tt>string</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод находит все включения подстроки поиска в исходной строке и заменяет их на подстроку замены.<br />
<br />
== insert ==<br />
<br />
insert(''позиция'': <tt>[[string_iterator]]</tt>, ''строка'': <tt>string</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод вставляет переданную строку в исходную, в месте, указанном итератором.<br />
<br />
== insert_int ==<br />
<br />
insert(''позиция'': <tt>[[int]]</tt>, ''строка'': <tt>string</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
То же, что и метод <tt>[[#insert|insert]]</tt>, только позиция передается числом.<br />
<br />
== erase ==<br />
<br />
erase(''начальная позиция:'' <tt>[[string_iterator]]</tt>, ''количество символов'': <tt>[[string_iterator]]</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод вырезает из исходной строки соответствующую подстроку.<br />
<br />
== erase_int ==<br />
<br />
erase_int(''начальная позиция:'' <tt>[[int]]</tt>, ''количество символов'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
То же, что и метод <tt>[[#erase|erase]]</tt>, только позиция передается числом.<br />
<br />
== find ==<br />
<br />
find(''подстрока поиска'': <tt>string</tt>, <span style="background-color: #EBEBEB;">''начальная позиция'': <tt>[[string_iterator]]</tt></span>) <br><br />
'''Возвращает''': <tt>[[string_iterator]]</tt> <br><br />
<br />
Метод ищет в исходной строке искомую подстроку и возвращает итератор начала вхождения. Для поиска подстроки, начиная с некоторой позиции, может быть передан второй параметр — итератор, установленный на нужную позицию.<br />
<br />
== find_first_of ==<br />
<br />
== find_first_not_of ==<br />
<br />
== begin ==<br />
<br />
'''Возвращает''': <tt>[[string_iterator]]</tt> <br><br />
<br />
Метод возвращает указатель на начало строки.<br />
<br />
== end ==<br />
<br />
'''Возвращает''': <tt>[[string_iterator]]</tt> <br><br />
<br />
Метод возвращает указатель на конец строки.<br />
<br />
'''Примечание''': Под "концом строки" здесь понимается позиция за последним символом строки. Таким образом, данный итератор не указывает на данные строки, а служит только как маркер конца строки. Конечно, при желании, можно использовать оператор <tt>[[string_iterator#оператор --|string_iterator:--]]</tt>, для движения по строке в обратную сторону.</div>Raw mathttp://man.deeptown.org/index.php/UidUid2013-07-13T10:37:15Z<p>Raw mat: </p>
<hr />
<div>Класс <tt>uid</tt> является представлением уникальных идентификаторов в проекте Диптаун. Каждый идентификатор — это 128-битное число. Класс поддерживает операции преобразования к общеупотребительным типам, а так же операции сравнения, которые можно понимать в математическом смысле, как сравнение двух чисел (больших чисел, надо сказать :).<br />
<br />
'''Родители''': <tt>[[Object]]</tt> <br><br />
'''Приводится к типам''': <tt>[[string]] [[bytea]]</tt> <br><br />
'''Реализует операторы''': [[оператор присваивания|присваивания]], [[операторы сравнения|сравнения]] <br><br />
<br />
<br />
При преобразовании к строке, идентификаторы представляются в виде <tt>'aaaa-bbbb-cccccccc-xxxxxxxxxxxxxxxx'</tt>, где каждая буква представляет собой шестнадцатиричную цифру.</div>Raw mathttp://man.deeptown.org/index.php/Release_NotesRelease Notes2013-07-13T10:36:01Z<p>Raw mat: </p>
<hr />
<div><H2>''' Старьё! ''' </H2><br />
<br />
<br />
Данная страница отражает текущее состояние Deeptown SDK и его компонентов. Содержание этой страницы формируется из важных тонкостей, которые разработчики считают нужным донести до пользователей, а также из ответов на часто задаваемые вопросы по текущему релизу.<br />
<br />
Прежде чем написать [[Bugzilla|отчет об ошибке]], проверьте - нет ли этой ошибки в данном списке.<br />
<br />
== Ядро ==<br />
<br />
* остается неисправленной ошибка с корректным завершением работы. Проблема кроется в сетевом движке.<br />
<br />
== Компилятор [[K++]] ==<br />
<br />
* исходный код стандартной библиотеки K++: [http://dao.deeptown.org/misc/kpp.kpp kpp.kpp]<br />
* не обрабатываются спецификаторы доступа. Т.е. все, что может быть экспортировано из класса или из модуля - экспортируется из него, а ключевые слова private, protected и public игнорируются.<br />
* для объектов, работающих с консолью (STDIN, STDOUT, STDERR) в стандартной библиотеке не реализованы оберточные более удобные классы. Это связано с тем, что эти переменные устанавливаются только для текущего модуля, и из стандартной библиотеки недоступны. Ошибка будет исправлена путем изменения механизма передачи этих объектов пользовательскому коду.<br />
* не доработан интерфейс класса string в плане удобства его использования.<br />
<br />
== Подсветка синтаксиса ==<br />
<br />
Пользователи KDE, для удобства работы с исходными текстами программ, могут установить написанные нами схемы подсветки синтаксиса для Katepart. После установки схем подсветки, все редакторы, которые используют этот виджет (Kate, KWrite, KDevelop, Krusader и др.), автоматически смогут правильно подсвечивать синтаксис исходных текстов, написанных на K++ и на Gide, а так же расставлять маркеры для свертки кода (code folding). <br />
<br />
Чтобы установить схемы подсветки, необходимо скачать их XML описания:<br />
* [http://dao.deeptown.org/misc/kpp.xml Для K++]<br />
* [http://dao.deeptown.org/misc/gide.xml Для Gide]<br />
<br />
А затем скопировать их в одну из нижеприведенных директорий:<br />
~/.kde/share/apps/katepart/syntax/<br />
<путь к системным файлам kde>/<версия>/share/apps/katepart/syntax/<br />
<br />
Узнать место установки kde в вашей системе, как правило можно с помощью переменной среды KDEDIRS. Если схема будет установлена в системную директорию, то она будет доступна всем пользователям, а не только текущему.</div>Raw mathttp://man.deeptown.org/index.php/Node_(DISS)Node (DISS)2013-07-13T10:31:50Z<p>Raw mat: </p>
<hr />
<div>Класс <tt>Node</tt> является базовым классом, представляющим элементы графа файловой системы [[DISS]]. Класс предоставляет общие для всех типов нод методы, например метод <tt>[[#path|path]]</tt>, возвращающий путь до данной ноды. Потомки класса <tt>Node</tt> представляют конкретные типы сущностей, например <tt>[[File]]</tt> или <tt>[[Directory]]</tt>.<br />
<br />
'''Родители''': <tt>[[Object]]</tt> <br><br />
'''Методы''': <tt><br />
[[#mountPoint|mountPoint]]<br />
[[#rights|rights]]<br />
[[#path|path]]<br />
[[#deleteTag|deleteTag]]<br />
[[#revision|revision]]<br />
[[#revision=|revision=]]<br />
[[#firstRevision|firstRevision]]<br />
[[#lastRevision|lastRevision]]<br />
[[#revisionBefore|revisionBefore]]<br />
[[#revisionAfter|revisionAfter]]<br />
[[#revisionCTime|revisionCTime]]<br />
[[#revisionATime|revisionATime]]<br />
[[#revert|revert]]<br />
[[#delete|delete]]</tt><br><br />
<br />
'''Операторы''': [[операторы индексного доступа]]<br />
<br />
__NOTOC__<br />
<br />
== операторы индексного доступа ==<br />
<br />
operator [] <br><br />
'''Возвращает''': <tt>[[array]]<[[string]]></tt> <br><br />
<br />
Оператор индексного чтения возвращает массив строк, содержащих тэги метаинформации.<br />
<br />
<br />
operator []= (''ключ'': <tt>[[string]]</tt>, ''значение'': <tt>[[string]]</tt>) <br><br />
operator []= (''ключ'': <tt>[[string]]</tt>, ''значение'': <tt>[[array]]<[[string]]></tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Оператор индексной записи записывает метаинформацию по указанному ключу. В качестве значения может быть передана либо строка, либо массив строк.<br />
<br />
== mountPoint ==<br />
<br />
'''Возвращает''': <tt>[[MountPoint]]</tt> <br><br />
<br />
Метод возвращает точку монтирования, которая поддерживает данную ноду.<br />
<br />
== rights ==<br />
<br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает число, соответствующее правам доступа для данной ноды.<br />
<br />
== path ==<br />
<br />
'''Возвращает''': <tt>[[string]]</tt> <br><br />
<br />
Метод возвращает полный путь до данной ноды.<br />
<br />
== deleteTag ==<br />
<br />
deleteTag(''ключ'': <tt>[[string]]</tt>, <span style="background-color: #EBEBEB;">''значение'': <tt>[[string]]</tt></span>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод удаляет переданный тэг (ключ и все значения) из метаинформации данной ноды. Если передан второй параметр, то будет удалена только соответствующая пара ключ-значение.<br />
<br />
== revision ==<br />
<br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает текущую ревизию файла.<br />
<br />
== revision= ==<br />
<br />
revision= (''требуемая ревизия'': <tt>[[int]]</tt>)<br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод устанавливает текущую ревизию для ноды. Соответственно, при открытии файла на чтение будет открываться эта ревизия.<br />
<br />
== firstRevision ==<br />
<br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает номер первой из доступных (имеющихся) ревизий.<br />
<br />
== lastRevision ==<br />
<br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает номер последней из доступных (имеющихся) ревизий.<br />
<br />
== revisionBefore ==<br />
<br />
revisionBefore(''время'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает ревизию, созданную раньше указанного времени.<br />
<br />
== revisionAfter ==<br />
<br />
revisionAfter(''время'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает ревизию, созданную позднее указанного времени.<br />
<br />
== revisionCTime ==<br />
<br />
revisionAfter(''ревизия'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает время создания указанной ревизии.<br />
<br />
== revisionATime ==<br />
<br />
revisionAfter(''ревизия'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': <tt>[[int]]</tt> <br><br />
<br />
Метод возвращает время последнего доступа к указанной ревизии.<br />
<br />
== revert ==<br />
<br />
revert(''ревизия'': <tt>[[int]]</tt>) <br><br />
'''Возвращает''': текущий объект <br><br />
<br />
Метод совершает откат файла до указанной ревизии. Откат осуществляется путем создания новой ревизии на основе копии указанной.<br />
<br />
== delete ==<br />
<br />
Метод удаляет ноду из файловой системы. Удаляются все ревии и соответствующая метаинформация.</div>Raw mathttp://man.deeptown.org/index.php/MEINMEIN2013-07-13T10:30:08Z<p>Raw mat: </p>
<hr />
<div>На данной странице будет собрана информация по движку интерфейса MEIN (MEta INterface). На данный момент здесь размещен бессвязный материал, использующийся в разработке; в дальнейшем все будет структурировано.<br />
<br />
== Controls ==<br />
<br />
Контролы в MEIN - это низкоуровневые классы, предназначенные для отображения одной сущности. Контролы предоставляют унифицированный кросплатформенный интерфейс для управления.<br />
<br />
Каждый контрол<br />
* имеет набор свойств, которые могут задаваться через стили<br />
* может генерировать стандартный набор событий<br />
* имеет набор методов для управления.<br />
<br />
Каждый из контролов стандартного набора должен поддерживаться всеми серверами интерфейса. Внешний вид контролов на тех или иных серверах может сильно различаться (вплоть до использования различных графических элементов), но набор свойств, событий и методов должен сохраняться неизменным. Некоторые системы могут расширять этот набор, но не урезать его.<br />
<br />
Несмотря на то, что данное описание предполагает некую иерархию свойств контролов, реальные иерархии классов могут различаться. Т.е. главное - чтобы контрол предоставлял необходимый набор свойств, а на иерархию классов пользовательский код опираться не будет (поскольку используется динамический язык).<br />
<br />
* [[Базовые свойства контрола]]<br />
** общие<br />
** видимые элементы<br />
** контейнеры<br />
* [[Группы верхнего уровня]]<br />
** Frame<br />
** Dialog<br />
* [[Логические группы]]<br />
** BoxSizer<br />
** GridSizer<br />
** Spacer<br />
* [[Управляющие группы]]<br />
** Panel<br />
** StaticBox<br />
** Splitter<br />
** StatusBar<br />
** ToolBar<br />
** Notebook<br />
** Scroller (scrolled window)<br />
* [[Диалоги]]<br />
** MessageDialog<br />
** TextEntryDialog<br />
* [[Элементы управления]]<br />
** Animation<br />
** Button<br />
** BitmapButton<br />
** ToggleButton<br />
** CheckBox<br />
** CheckListBox<br />
** Choice<br />
** ComboBox<br />
** Gauge<br />
** ListBox<br />
** StringCtrl (single-line string)<br />
** TextCtrl (multiline string)<br />
** SpinCtrl<br />
** StaticText<br />
** Label<br />
** Hyperlink<br />
** Bitmap<br />
** RadioButton<br />
* [[Меню]]<br />
** Menu<br />
** MenuBar<br />
** MenuItem</div>Raw mathttp://man.deeptown.org/index.php/Deptown_Programming_Contest_(DPC)Deptown Programming Contest (DPC)2013-07-13T10:25:18Z<p>Raw mat: </p>
<hr />
<div>Здравствуй Гость! Ты попал на страничку программерского соревнования в рамках проекта Диптаун =)<br />
<br />
DPC это соревнование, участие в котором принимают роботы, находящиеся в виртуальном пространстве диптауна. Управляет роботом программа, написанная на языке [[K++]].<br />
<br />
Идея соревнования была заимствована из проекта [http://robocode.sourceforge.net/ Robocode] и адаптирована под текущие реалии и специфику платформы Диптаун. <br />
<br />
<br />
== Для чего это нужно? ==<br />
<br />
Если говорить откровенно, то мы еще сами не знаем зачем все это и что из этого может получиться :)<br />
<br />
Но на самом деле, у DPC целей много. Во-первых, популяризация самого проекта Диптаун и наглядная демонстрация возможностей платформы Диптауна. Во-вторых, это отличный способ изучить язык [[K++]] в игровой обстановке, а так же попробовать себя в решении нечетких задач и умения находить нестандартные решения. В-третьих, лучший способ отладить некую систему, это начать активно ее использовать. Заодно, мы сможем лучше представить, какие функции более всего востребованы разработчиками, что использовать удобно, а что требуется изменить, ну и так далее.<br />
<br />
Наконец, это просто забавно и жутко интересно :)<br />
<br />
== Основная идея ==<br />
<br />
Есть некая арена, в пространстве которой существуют боевые роботы. Каждый робот принадлежит одному из участников. На каждом роботе крутится программа управления этим роботом. Цель -- победить соперников и остаться в живых. Третьего не дано :) <br />
<br />
== Правила соревнований ==<br />
<br />
DPC это в первую очередь соревнование мозгов, а потом уже случайностей и прочих факторов. Поэтому, влияние случайностей должно быть сведено к минимуму. Однако убрать их совсем не получится, опять же в силу специфики платформы, да это и не нужно. Впрочем, нельзя исключать и элемент зрелищности, где случай может сыграть свою роль. Поэтому на данный момент у меня есть несколько вариантов игры :)<br />
<br />
На данный момент, я их представляю следующим образом: <br />
<br />
== Вариант первый: Sumo Challenge ==<br />
<br />
[[Изображение:DPC.png|right]]<br />
В этом варианте соревнований самые простые правила и роботы. Однако это не значит что победить в нем легко. Наоборот, мне кажется что это лучший из вариантов, поскольку все зависит от тактики робота и качества программы. <br />
<br />
=== Арена ===<br />
<br />
Арена -- это пространство для соревнования. Арена зависит от типа проводимого соревнования. В данном случае это площадка без ограждений, размерами скажем 10 на 10 метров. Сама площадка может висеть над пустотой или просто являться возвыщением. <br />
<br />
В определенных местах на нее помещаются два робота соперника. По сингалу роботы начинают движение. Основная задача -- вытолкнуть соперника за пределы площадки, самому оставшись на ней. Роботы полностью идентичны, они состоят из одинаковых компонентов и имеют одинаковые параметры мощности и скорости. Единственное отличие в программе управления. Более умная программа должна учитывать положение соперника и нападать, в то же время стараясь не подставляться под удар.<br />
<br />
<br />
=== Роботы ===<br />
<br />
Sumo это простейший вид соревнований, соответственно и роботы должны быть простые :) На данный момент планируется 2 вида роботов: четырех колесные и трехколесные. Первый имеет два рулевых колсеса являющиеся так же ведущими (машина с передним приводом), второй -- два ведущих и одно рулевое (трайк).<br />
<br />
Трехколесный робот более маневренный, однако и менее устойчивый. Четырехколесный более устойчивый, но управлять им сложнее. <br />
<br />
<br />
=== Управление ===<br />
<br />
Независимо от конструкции, все роботы обладают следущим набором систем:<br />
* Приводы колес (моторы). Можно задавать скорость вращения и угол поворота (для ведущих колес).<br />
* Сенсор краев -- с его помощью можнно определять положение робота на площадке<br />
* Радар -- служит для слежения за роботом противника. Дает информацию о его скорости и направлении движения.<br />
<br />
Дополнительно, сам робот знает скорость своего движения и свое положение в пространстве арены.<br />
<br />
<br />
=== Стратегии ===<br />
<br />
Постараюсь привести пример нескольких стратегий поведения робота, а также возможные контр стратегии:<br />
<br />
==== Берсерк ====<br />
<br />
Наверное самая ломовая стратегия :) Просто фиксируем положение противника и гоним в него, в надежде что так мы его скинем с площадки. <br />
<br />
* Плюсы: Простота кода :)<br />
* Минусы: Если противник не дурак, то уже после первой игры можно придумать как уворачиваться от такого. Не забывайте, что если берсерк достаточно разгонится, то затормозить он уже не успеет и просто улетит за пределы площадки, в результате проиграет :)<br />
<br />
<br />
==== Трус ====<br />
<br />
Стратегия, обратная "берсерку". Стараемся всячески избегать противника и находиться от него подальше. <br />
<br />
* Плюсы: выиграть сложно, но зато сам не улетишь. При определенных условиях, грамотно написанный "трус", будет выигрывать у "берсерка", поскольку последний сам улетит :)<br />
* Минусы: если ваш противник не берсерк, то выиграть будет практически невозможно<br />
<br />
<br />
==== Тормоз ====<br />
<br />
Данная стратегия направлена на максимальное затягивание времени ведущее к ничьей. Робот может нарезать круги по площадке, одновременно сочетая в себе черты труса.<br />
<br />
* Плюсы: если ваша цель -- ничья, то можно подумать об этом варианте :)<br />
* Минусы: умрем раньше, чем два "тормоза" на арене завершат бой <br />
<br />
<br />
==== А что дальше? ====<br />
<br />
Важно понимать, что вышеописанные стратегии являются лишь типовыми шаблонами. Настоящий боевой алгоритм должен уметь сочетать в себе всех трех персонажей. Например, он может вести себя как трус, уворачиваясь от ударов агрессивного соперника, в то же время если сложилась такая ситуация что соперник оказался на самом краю площадки, такой робот может перейти в режим наступления и тем самым выиграть.<br />
<br />
В общем случае стратегий можно придумать бесконечное количество :) Все ограничивается лишь вашей фантазией, временем и желанием :)<br />
<br />
<br />
=== Очки ===<br />
<br />
За каждую победу начисляется одно очко, за поражение ноль.<br />
<br />
Для предотвращения затягивания боя, вводится трехминутное ограничение на длину боя. Если по истечении этого времени оба робота остались на площадке, бой завершается вничью. Для большей статистической значимости возможно проведение нескольких боев, после чего победившим считается соперник, набравший большее количество очков.<br />
<br />
<br />
== Вариант второй -- классическая Robocode ==<br />
<br />
Здесь на первое место выходит зрелищность :) Действие происходит на поле, на котором может находиться сразу несколько соперников. Каждый соперник представлен танком, имеющим пушку и радар. По условиям, танки так же идентичны. <br />
<br />
На поле боя могут находиться вспомогательные элементы вроде стен или возвышений. Возвышения позволяют стрелять дальше, если въехать на них, стены помогают защищаться от вражеских выстрелов.<br />
<br />
Возможных вариантов стратегий здесь еще больше чем в Sumo. Описывать их все не хватит времени, тем более что по этой теме есть масса материалов на [http://robocode.sourceforge.net/ официальном сайте].<br />
<br />
<br />
== Текущее положение дел ==<br />
<br />
--[[Участник:Velaar|Velaar]] 02:56, 18 мая 2009 (NOVST)<br />
Всех желающих принять участие в реализации проекта просим в [http://ding.deeptown.org Deeptown DING project].<br />
<br />
Как только перейдем к этому проекту, появится соотвествующая новость и запись здесь.</div>Raw mathttp://man.deeptown.org/index.php/%D0%91%D0%B0%D0%B3%D1%82%D1%80%D0%B5%D0%BA%D0%B5%D1%80Багтрекер2013-07-13T10:18:13Z<p>Raw mat: </p>
<hr />
<div>Если Вы нашли ошибку в Deeptown SDK, пожалуйста, сообщите о ней разработчикам. Сделать это можно по адресу [http://bugs.deeptown.org http://bugs.deeptown.org].<br />
<br />
Чтобы сэкономить время разработчиков и уменьшить время исправления ошибки, пожалуйста, приложите как можно более подробное ее описание. Как минимум, требуется:<br />
* последовательность действий, приводящих к ошибке<br />
* если это ошибка в компиляторе - код или часть кода, которая ведет себя неправильно или приводит к ошибке компилятора;<br />
<br />
'''не закончено'''.</div>Raw mathttp://man.deeptown.org/index.php/DirectoryDirectory2011-08-29T03:47:53Z<p>Raw mat: </p>
<hr />
<div>Класс <tt>Directory</tt> представляет одну директорию файловой системы [[DISS]]. <br />
<br />
'''Родители''': [[Node (DISS)|Node]] <br><br />
'''Методы''': <tt>[[#items|items]]</tt><br />
<br />
__NOTOC__<br />
<br />
== items ==<br />
<br />
'''Возвращает''': <tt>[[array]]<[[Node (DISS)|Node]]></tt><br />
<br />
Метод возвращает содержимое директории в виде массива потомков <tt>[[Node (DISS)|Node]]</tt>.</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BB%D0%B0%D1%82%D1%84%D0%BE%D1%80%D0%BC%D1%8B_GideОписание платформы Gide2010-09-09T17:57:22Z<p>Raw mat: Правки 174.129.40.254 (обсуждение) откачены к версии Raw mat</p>
<hr />
<div>== Введение ==<br />
<br />
=== Роль скриптов в проекте ===<br />
<br />
Многие объекты виртуального мира обладают некоторым специфичным поведением. Например, при нажатии на кнопку зажигается лампочка, а для открытия некоторых дверей требуется ключ (пароль). Для того, чтобы задать специфичное поведение объекту, предусмотрено два способа:<br />
<br />
# написание плагина к физическому движку Диптауна, расширяющего стандартное дерево классов объектов;<br />
# написание скрипта на специализированном языке программирования.<br />
<br />
Еще одна важная область применения скриптов - т.н. процедурные модели и текстуры. Суть их заключается в следующем.<br />
<br />
Как известно, классически текстура задается в виде массива чисел, каждое из которых определяет цвет соответствующей точки. Даже при достаточно хорошем сжатии, текстуры имеют большой размер и их очень "дорого" передавать по сети. Альтернативой являются процедурные текстуры. Процедурная текстура задается некоторой программой (скриптом), которая рисует изображение. При этом по сети передается относительно маленькая программа, которая затем исполняется на машине пользователя, а полученное изображение используется в качестве текстуры. <br />
<br />
Аналогично, процедурная модель - это скрипт, создающий трехмерную модель. <br />
<br />
Итак, скрипты в проекте используются, в основном, в трех направлениях: <br />
* задание поведения объектов; <br />
* создание процедурных моделей; <br />
* создание процедурных текстур.<br />
<br />
=== Терминология ===<br />
<br />
В этом документе будут использованы следующие термины: <br />
* Gide (гайд) - единый низкоуровневый язык; <br />
* байт-код gide - бинарный код, получаемый после компиляции кода gide; <br />
* виртуальная машина gide - программа, исполняющая байт-код. <br />
<br />
=== Основные концепции ===<br />
<br />
Для различных задач удобны различные языки и различные наборы функций. Поэтому, во избежание повторного написания низкоуровневых библиотек, а также для удобства программирования скриптовых языков, было решено разработать единый низкоуровневый язык - gide. Код любого другого языка компилируется в код gide, затем компилятор gide создает платформенно-независимый байт-код, который исполняется виртуальной машиной gide. Механизм компиляции и исполнения кода изображен на рис. 1. <br />
<br />
[[Image:gidebc-1.gif|thumb|Рис. 1. Механизм компиляции и исполнения кода]]<br />
<br />
Преимущества такого подхода очевидны: <br />
* использование библиотек одного языка другими; <br />
* единая виртуальная машина для всех языков. <br />
<br />
Язык gide берет за основу модульную архитектуру. Модули gide - это динамически подключаемые библиотеки, которые бывают двух типов: <br />
# библиотеки, представляющие собой байт-код gide ('''управляемые''' модули);<br />
# "обычные" динамически-подключаемые библиотеки ('''неуправляемые''' модули).<br />
<br />
Любые из этих библиотек можно подключать как статически, так и динамически.<br />
Область задач gide:<br />
* расчет процедурных текстур, объектов и, возможно, звуков;<br />
* управление поведением объектов в виртуальном пространстве; <br />
* управление объектами интерфейса на сервере интерфейса.<br />
<br />
Gide должен предоставлять максимально удобный механизм управления "внешними" объектами. Набор таких объектов определяет набор решаемых задач, т.е. некий контекст того или иного скрипта.<br />
<br />
=== Модель языка ===<br />
<br />
Основополагающим понятием в модели Gide является объект.<br />
<br />
'''Объект''' - это некоторая абстрактная сущность, для которой определены свойства (поля) и операции (методы). Свойства объекта также являются (именованными) ссылками на объекты. И набор свойств, и набор операций - это динамические наборы, т.е. наличие того или иного свойства/операции определяется в момент выполнения, а не в момент компиляции. Gide позволяет оперировать ссылками на объекты. То есть единственный существующий в нем тип данных - это ссылка на объект. Ссылка может либо указывать на объект, либо быть равной 0.<br />
<br />
Набор методов объекта определяется его классом.<br />
<br />
'''Класс''' - это набор функций, каждая из которых имеет доступ к объекту, для которого она была вызвана. У класса может быть один или несколько '''родительских''' классов: при поиске метода, он ищется сначала в классе объекта, а затем - рекурсивно - во всех родительских классах, в порядке их перечисления.<br />
<br />
'''Функция''', в свою очередь - это некоторый набор операторов, выполняющих определенные действия и составляющие '''тело функции'''. Функция может быть методом какого-либо класса, либо отдельной функцией, не являющейся методом. Функция может принимать некоторое количество параметров и возвращать результат (объект).<br />
<br />
Набор классов, предоставляемых скрипту, формирует так называемый контекст исполнения, т.е. по сути стандартную библиотеку языка.<br />
<br />
Эта библиотека, для тех или иных вариантов использования, может включать в себя:<br />
* объекты стандартных типов данных:<br />
** целые числа,<br />
** вещественные числа,<br />
** строки,<br />
** идентификаторы объектов,<br />
** указатели,<br />
** массивы;<br />
* стандартные операции для этих объектов:<br />
** арифметические операции,<br />
** операции сравнения,<br />
** математические функции,<br />
** и пр.<br />
<br />
== Синтаксис языка gide ==<br />
<br />
Синтаксис языка Gide максимально упрощен - с рассчетом на то, что поверх него будут писаться реализации языков более высокого уровня.<br />
<br />
Каждая непустая строка исходного кода gide - это некоторая инструкция, записанная в следующей форме:<br />
ключевое_слово аргумент1, аргумент2, ..., аргументN<br />
<br />
Некоторые инструкции допустимы только за пределами тела функции, некоторые, наоборот, допустимы только внутри тела функции.<br />
<br />
Аргументы для инструкции - это произвольные текстовые строки. Если значение аргумента содержит управляющие символы (пробел, табуляция, перевод строки, запятая, символы # или ") - его необходимо заключить в двойные кавычки. Внутри таких кавычек также допустимы escape-последовательности \r, \n, \t, \" и \\.<br />
<br />
'''ЗАМЕЧАНИЕ''': вставка произвольных символов \xNN на данный момент не поддерживается, но запланирована на будущее.<br />
<br />
Весь текст от символа # и до конца строки - комментарий, он игнорируется.<br />
<br />
Внутри тела функции также возможно вставлять т.н. '''метки'''. Метка - это некоторая уникальная в пределах данной функции ссылка на инструкцию. Метка выглядит следующим образом:<br />
имя_метки:<br />
<br />
Она может быть записана как на отдельной строке, так и на строке с инструкцией.<br />
<br />
=== Подключение внешних модулей ===<br />
<br />
Для использования классов и функций, объявленных во внешних модулях (любого типа), необходимо их подключить, используя инструкцию '''use'''. Она записывается следующим образом:<br />
<br />
use ''адрес''<br />
<br />
Для неуправляемых модулей, адрес - это просто имя такого модуля.<br />
<br />
Для управляемых модулей, адрес - это префикс 'gbc:' плюс URL-адрес потока модуля. Например, "gbc:diss:/lib/module.gbc".<br />
<br />
Зависимости модулей - рекурсивные. Другими словами, если модуль B подключил модуль A, а модуль C подключил модуль B, то из модуля C будут доступны объявления модуля A.<br />
<br />
=== Объявление классов ===<br />
<br />
Для того, чтобы объявить класс, нужно написать следующую инструкцию:<br />
<br />
class ''имя_класса''<br />
<br />
Имя класса - это произвольная строка. Данная инструкция объявляет класс, в котором еще нет ни одного метода.<br />
<br />
Инструкция<br />
<br />
inherit ''имя_класса'', ''имя_родителя''<br />
<br />
добавляет класс ''имя_родителя'' в качестве родительского класса. Она должна быть записана после объявления класса. В этой инструкции, и класс ''имя_класса'', и класс ''имя_родителя'' могут быть объявлены во внешнем модуле.<br />
<br />
Существует также понятие родителя по-умолчанию. Они могут быть заданы только в неуправляемых модулях. Такие родители добавляются автоматически, если не указаны другие родительские классы.<br />
<br />
=== Объявление глобальных переменных ===<br />
<br />
Для того, чтобы объявить глобальную переменную - т.е. переменную, доступную из любой функции - нужно написать инструкцию<br />
<br />
global ''имя_переменной''<br />
<br />
В качестве имени переменной, опять же, может выступать любая строка.<br />
<br />
Глобальные переменные доступны только в пределах данного модуля. Для получения их значений из других модулей, следует использовать функции.<br />
<br />
По-умолчанию все глобальные переменные инициализируются нулем.<br />
<br />
=== Объявление функции ===<br />
<br />
Функция объявляется следующим образом:<br />
<br />
function ''имя_функции'', ''аргумент1'', ''аргумент2'', ..., ''аргументN''<br />
''тело функции''<br />
end<br />
<br />
Имя функции - это произвольная текстовая строка. Если имя имеет вид<br />
''имя_класса'':''имя_метода''<br />
, метод ''имя_метода'' добавляется в класс ''имя_класса'' (этот класс должен быть предварительно объявлен, но не обязательно в текущем модуле). В противном случае, объявляется функция, а не метод.<br />
<br />
Аргументы - это формальные параметры функции, они доступны в теле как локальные переменные.<br />
<br />
Список аргументов в какой-то мере условен. При вызове функции всегда можно указывать произвольное число параметров, это нигде не проверяется. Если фактических параметров меньше, чем формальных - оставшиеся аргументы будут инициализированы нулем. Если фактических больше - некоторые из них не будут доступны напрямую, однако стандартная библиотека может предоставлять функции, открывающие к ним доступ.<br />
<br />
Методы класса могут быть публичными (public), защищенными (protected) или частными (private). Публичные методы можно вызывать без ограничений, защищенные - только из данного класса и его потомков, частные - только из данного класса. Эти проверки делаются во время исполнения.<br />
<br />
По-умолчанию, метод является публичным. Для объявления защищенных и частных методов, нужно заменить ключевое слово function в объявлении метода на '''func_protected''' и '''func_private''' соответственно.<br />
<br />
Если метод/функция уже был объявлен ранее, он переобъявляется - т.е. данная реализация перекрывает предыдущую. Вызвать предыдущую реализацию можно при помощи оператора '''recall''' (см. ниже).<br />
<br />
Функция со специальным именем '''@@module_init''' является конструктором модуля; она вызывается в момент загрузки данного модуля. В ней, в частности, можно инициализировать глобальные переменные модуля.<br />
<br />
Метод '''@@init''' - конструктор класса. Он вызывается в момент создания объекта данного класса. Порядок вызова конструкторов таков: сначала рекурсивно вызываются конструкторы родительских классов в порядке объявления родителей, затем - конструктор данного класса. В конструкторе, в частности, можно инициализировать поля объекта.<br />
<br />
=== Специальные переменные ===<br />
<br />
Существует 6 зарезервированных имен переменных:<br />
* '''0''' (число ноль) - означает отсутствие объекта;<br />
* '''@true''' - указывает на специальный объект "истина". Этот объект не имеет полей и методов; он бывает полезен для написания логических конструкций;<br />
* '''@false''' - то же, что и 0;<br />
* '''@this''' - внутри функции-метода, указывает на текущий объект; внутри обычной функции равен 0;<br />
* '''@result''' - результат последней вызванной функции или оператора '''access''';<br />
* '''@exception''' - объект исключения, которое требуется обработать.<br />
<br />
=== Операторы функции ===<br />
<br />
'''Операторы''' - это управляющие инструкции, составляющие тело функции.<br />
<br />
Поскольку единственный существующий тип переменной - это указатель на объект, значительная часть операторов не требуется. Все операторы реализуются на уровне самих объектов в виде методов.<br />
<br />
Существуют следующие операторы:<br />
<br />
new ''переменная'', ''имя_класса'', ''строка''<br />
<br />
: создает новый объект класса ''имя_класса'' и записывает результат в указанную переменную. Если переменная была объявлена ранее - результат записывается в нее; если нет - создается новая '''локальная''' переменная (т.е. переменная, доступная только в пределах данной функции). Третий параметр - это некоторые данные для инициализации объекта. Они имеют значение только для некоторых классов в неуправляемых модулях. Кроме того, любой класс может быть инициализирован пустой строкой.<br />
<br />
bless ''переменная'', ''имя_класса''<br />
<br />
: эквивалент <tt>new</tt> без последнего параметра.<br />
<br />
mov ''переменная'', ''источник''<br />
<br />
: копирует в переменную ''переменная'' ссылку на существующий объект ''источник'' (аналогично, если переменная не была объявлена ранее - создается новая локальная переменная). Важно подчеркнуть, что при этой операции не создается нового объекта; две переменные будут указывать на один и тот же объект.<br />
<br />
goto ''имя_метки''<br />
<br />
: осуществляет безусловный переход к оператору, помеченному меткой ''имя_метки''.<br />
<br />
if ''переменная'', ''имя_метки''<br />
<br />
: осуществляет условный переход к оператору ''имя_метки'': если управляющая переменная (первый параметр) имеет отличное от нуля значение, осуществляется переход; в противном случае этот оператор игнорируется и выполнение продолжается со следующей инструкции.<br />
<br />
call ''переменная'', ''имя_функции'', ''аргумент1'', ..., ''аргументN''<br />
<br />
: если ''переменная'' - '''0''', вызывает функцию ''имя_функции''; в противном случае вызывает метод объекта, на который ссылается переменная. Как уже говорилось ранее, количество аргументов может быть любым вне зависимости от количества формальных параметров. Результат выполнения функции записывается в специальную переменную '''@result'''.<br />
<br />
: ''имя_функции'' может быть задано в форме ''имя_класса'':''имя_метода''. В этом случае осуществляется вызов метода конкретного класса, а не текущего (''имя_класса'' должно либо совпадать с классом объекта, либо быть его родительским классом). Это позволяет вызывать методы родительских классов, несмотря на перекрытие в дочерних классах.<br />
<br />
return ''переменная''<br />
<br />
: осуществляет выход из текущей функции с возвратом объекта, на который ссылается ''переменная''. Если выполнение функции доходит до конца, функция возвращает 0.<br />
<br />
access ''переменная'', ''имя_поля''<br />
<br />
: получает значение поля ''имя_поля'' объекта, на который ссылается ''переменная''. Значение записывается в специальную переменную '''@result'''. Если указанного поля в классе нет, в '''@result''' записывается 0.<br />
<br />
mutate ''переменная'', ''имя_поля'', ''значение''<br />
<br />
: записывает объект, на который ссылается ''значение'', в поле ''имя_поля'' объекта, на который ссылается ''переменная''. Если такого поля ранее не существовало, оно создается.<br />
<br />
recall<br />
<br />
: вызывает замещенную функцию с теми же параметрами, которые были переданы данной функции; осуществляет выход из текущей функции с результатом, который вернула замещенная функция.<br />
<br />
upgrade ''переменная'', ''имя_класса''<br />
<br />
: расширяет объект, на который ссылается ''переменная'', до класса ''имя_класса''. Это делается путем вызова конструкторов классов, которые в дереве иерархии находятся между классом переменной и указанным классом. Очевидно, класс объекта должен быть родительским для класса ''имя_класса''. После выполнения данной инструкции, класс объекта станет равным ''имя_класса'', т.е. для него будут доступны все методы этого класса.<br />
<br />
use_locals ''имя_функции''<br />
<br />
: открывает данной функции доступ к локальным переменным функции ''имя_функции''. Этот оператор может быть указан только в самом начале тела функции и только единожды. ''имя_функции'' записывается как при объявлении: для обычной функции - просто ее имя, для метода класса - имя класса и имя метода, разделенные двоеточием. Для того, чтобы локальные переменные реально стали доступны, функция ''имя_функции'' должна вызвать текущую функцию специальным образом.<br />
<br />
: '''ПРИМЕЧАНИЕ''': такой специальный вызов на данный момент невозможно сделать напрямую. Класс [[Closure]] стандартной библиотеки позволяет создать указатель на метод, при вызове которого (через этот указатель) происходит требуемый тип вызова. В будущем планируется добавить оператор вызова '''ccall''', выполняющий эту операцию.<br />
<br />
: данная инструкция требуется для реализации [http://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%BC%D1%8B%D0%BA%D0%B0%D0%BD%D0%B8%D0%B5_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29 замыканий].<br />
<br />
Кроме перечисленных, существуют еще операторы для работы с исключениями; они рассмотрены в следующем подразделе.<br />
<br />
=== Обработка исключений ===<br />
<br />
'''Исключения''' - это специальный механизм, предназначенный для передачи информации об ошибках. Он заключается в следующем.<br />
<br />
Любая функция может бросить исключение. Само исключение - это некоторый объект произвольного класса. Когда такое происходит, виртуальная машина начинает искать ближайший обработчик исключений. Если сама функция, бросившая исключение, может его обработать - исполнение передается соответствующей инструкции. В противном случае, начинается раскрутка стека вызовов до первой функции, которая может обработать исключение. Если ни одна функция этого сделать не может, выполнение программы прекращается, а вызвавшему модулю сообщается о необработанном исключении.<br />
<br />
В функции может быть установлено несколько обработчиков исключений, которые формируют стек. Обработчик - это некоторая метка в пределах функции. Когда функции требуется обработать исключение, управление передается оператору, на который ссылается эта метка, а сама эта метка извлекается из стека обработчиков. Другими словами, в функции может быть установлено несколько обработчиков, которые, при необходимости, будут вызываться в порядке, обратном их установке. Если же возникает исключение, а в функции не установлено ни одного обработчика - происходит выход из функции, а исключение передается функции, которая вызвала данную функцию, и т.д.<br />
<br />
Для работы с исключением существуют следующие четыре оператора:<br />
<br />
throw ''переменная''<br />
<br />
: кидает исключение, передав в качестве параметра объект, на который ссылается ''переменная''.<br />
<br />
except_push ''имя_метки''<br />
<br />
: добавляет метку ''имя_метки'' в стек обработчиков исключений для данной функции. Когда возникает исключение, управление передается указанной метке, а объект исключения записывается в специальную переменную '''@exception'''.<br />
<br />
except_pop<br />
<br />
: изымает из стека обработчиков метку, находящуюся на вершине стека.<br />
<br />
except_clr<br />
<br />
: очищает стек обработчиков исключений для данной функции.<br />
<br />
Кроме оператора '''throw''', исключения могут кидаться неуправляемыми модулями. Кроме того, существует механизм, при помощи которого ошибки, возникающие на уровне самой виртуальной машины, передаются в стандартную библиотеку, которая кидает исключение.<br />
<br />
== Формат байт-кода gide ==<br />
<br />
<font color="red">'''ПРИМЕЧАНИЕ:''' в данном разделе приведена устаревшая информация, которая не соответствует действительности. Раздел оставлен для ознакомления с общей концепцией хранения байт-кода. Через некоторое время он будет переписан.</font><br />
<br />
Байт-код gide представляет собой популярный BER-формат: весь файл разбит на секции, каждая из которых начинается с описания ее типа и размера. <br />
<br />
Следует сразу оговориться, что все числа в gide представляются в формате big endian (т.е. наименее значимый байт впереди). <br />
<br />
Заголовок BER-секции представляет собой последовательность кода секции (1 байт) и ее размер в BER-формате.<br />
<br />
Все целые числа в байт-коде, если это не оговорено специально, представляются в BER-формате: для записи числа требуется столько байт, сколько нужно; в каждом байте записывается 7 битов числа. Если самый старший бит равен 1, требуется прочитать/записать следующий байт. Если старший бит равен 0 - это последний байт, описывающий число.<br />
<br />
=== Секция имен ===<br />
<br />
Тип секции - код символа N.<br />
<br />
Для большей компактности кода используется секция имен. В ней последовательно записываются все текстовые строки (имена переменных/функций, данные конструкторов и пр.), которые встречаются в скрипте. Одинаковые текстовые строки записываются только один раз. Таким образом, каждая текстовая строка получает свой уникальный номер, и в байт-коде вместо строк используются эти номера.<br />
<br />
Поскольку в скрипте часто встречаются одинаковые строки (например, работа с одной и той же переменной), это сильно уменьшает размер кода.<br />
<br />
Все строки записываются следующим образом:<br />
* длина строки (в BER формате, см. выше);<br />
* текст.<br />
<br />
Нумерация строк начинается не с нуля, а с 16. Меньшие номера зарезервированы для стандартных имен:<br />
* Переменная @null (она же 0) имеет номер 0;<br />
* Переменная @this имеет номер 1;<br />
* Переменная @false имеет номер 2;<br />
* Переменная @true имеет номер 3.<br />
<br />
В случае обращения к полю объекта, в байт-код записывается все составное имя объекта (вместе с разделяющими точками). Виртуальная машина сама разбирает составные имена.<br />
<br />
=== Секция экспорта ===<br />
<br />
Тип секции - ASCII-код символа E<br />
<br />
Данная секция предназначена для упрощения навигации по секции кода.<br />
<br />
Для каждой функции, описанной в модуле, записывается:<br />
* индекс имени функции;<br />
* смещение функции относительно начала секции кода.<br />
<br />
=== Секция кода ===<br />
<br />
Тип секции кода - ASCII-код символа C.<br />
<br />
В эту секцию записываются тела функций Gide.<br />
<br />
Для каждой функции записывается:<br />
* индекс имени функции (он должен быть смещен от начала секции кода на количество байт, указанных в секции экспорта);<br />
* количество аргументов функции;<br />
* индексы имен аргументов функции;<br />
* размер блока операторов;<br />
* для каждого оператора:<br />
** индекс имени оператора;<br />
** количество передаваемых параметров функции (только для оператора call);<br />
** индексы имен всех параметров.<br />
<br />
Индексы операторов:<br />
* Простые операторы имеют номера от 1 до 9 в порядке их перечисления в п.2.1;<br />
* Оператор создания объекта имеет номер 10;<br />
* Оператор throw имеет номер 13;<br />
* Оператор except_push имеет номер 16;<br />
* Оператор except_pop имеет номер 17;<br />
* Оператор создания переменной пользовательского класса имеет номер 11;<br />
* Оператор расширения наследования имеет номер 15;<br />
* Оператор use имеет номер 12;<br />
* Оператор external номер 14;<br />
<br />
=== Секция импорта ===<br />
<br />
Тип секции импорта - ASCII-код символа I<br />
<br />
В эту секцию записываются подключаемые операторами use и external библиотеки.<br />
<br />
Для каждой библиотеки записывается:<br />
* число 1 в случае оператора external, 0 - в случае use;<br />
* индекс пути к библиотеке.<br />
<br />
=== Секция наследования классов ===<br />
<br />
Тип секции - код символа H.<br />
<br />
В эту секцию записывается статическая таблица наследований классов.<br />
<br />
Для каждого оператора inherit записывается:<br />
* индекс наследующего класса;<br />
* индекс наследуемого класса.<br />
<br />
=== Секция номеров строк ===<br />
<br />
Тип секции - код символа D.<br />
<br />
Эта секция не является обязательной. В ней записываются соответствия операторов и номеров строк в исходных файлах, на которых указан соответствующий оператор.<br />
<br />
В секцию записывается массив следующих структур:<br />
* индекс имени файла;<br />
* размер блока функций;<br />
* для каждой функции:<br />
** индекс имени функции;<br />
** размер блока операторов;<br />
** для каждого оператора:<br />
*** номер оператора в теле функции;<br />
*** номер строки кода, на которой объявлен оператор.</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BB%D0%B0%D1%82%D1%84%D0%BE%D1%80%D0%BC%D1%8B_GideОписание платформы Gide2010-08-31T19:15:43Z<p>Raw mat: Правки 96.57.154.179 (обсуждение) откачены к версии Raw mat</p>
<hr />
<div>== Введение ==<br />
<br />
=== Роль скриптов в проекте ===<br />
<br />
Многие объекты виртуального мира обладают некоторым специфичным поведением. Например, при нажатии на кнопку зажигается лампочка, а для открытия некоторых дверей требуется ключ (пароль). Для того, чтобы задать специфичное поведение объекту, предусмотрено два способа:<br />
<br />
# написание плагина к физическому движку Диптауна, расширяющего стандартное дерево классов объектов;<br />
# написание скрипта на специализированном языке программирования.<br />
<br />
Еще одна важная область применения скриптов - т.н. процедурные модели и текстуры. Суть их заключается в следующем.<br />
<br />
Как известно, классически текстура задается в виде массива чисел, каждое из которых определяет цвет соответствующей точки. Даже при достаточно хорошем сжатии, текстуры имеют большой размер и их очень "дорого" передавать по сети. Альтернативой являются процедурные текстуры. Процедурная текстура задается некоторой программой (скриптом), которая рисует изображение. При этом по сети передается относительно маленькая программа, которая затем исполняется на машине пользователя, а полученное изображение используется в качестве текстуры. <br />
<br />
Аналогично, процедурная модель - это скрипт, создающий трехмерную модель. <br />
<br />
Итак, скрипты в проекте используются, в основном, в трех направлениях: <br />
* задание поведения объектов; <br />
* создание процедурных моделей; <br />
* создание процедурных текстур.<br />
<br />
=== Терминология ===<br />
<br />
В этом документе будут использованы следующие термины: <br />
* Gide (гайд) - единый низкоуровневый язык; <br />
* байт-код gide - бинарный код, получаемый после компиляции кода gide; <br />
* виртуальная машина gide - программа, исполняющая байт-код. <br />
<br />
=== Основные концепции ===<br />
<br />
Для различных задач удобны различные языки и различные наборы функций. Поэтому, во избежание повторного написания низкоуровневых библиотек, а также для удобства программирования скриптовых языков, было решено разработать единый низкоуровневый язык - gide. Код любого другого языка компилируется в код gide, затем компилятор gide создает платформенно-независимый байт-код, который исполняется виртуальной машиной gide. Механизм компиляции и исполнения кода изображен на рис. 1. <br />
<br />
[[Image:gidebc-1.gif|thumb|Рис. 1. Механизм компиляции и исполнения кода]]<br />
<br />
Преимущества такого подхода очевидны: <br />
* использование библиотек одного языка другими; <br />
* единая виртуальная машина для всех языков. <br />
<br />
Язык gide берет за основу модульную архитектуру. Модули gide - это динамически подключаемые библиотеки, которые бывают двух типов: <br />
# библиотеки, представляющие собой байт-код gide ('''управляемые''' модули);<br />
# "обычные" динамически-подключаемые библиотеки ('''неуправляемые''' модули).<br />
<br />
Любые из этих библиотек можно подключать как статически, так и динамически.<br />
Область задач gide:<br />
* расчет процедурных текстур, объектов и, возможно, звуков;<br />
* управление поведением объектов в виртуальном пространстве; <br />
* управление объектами интерфейса на сервере интерфейса.<br />
<br />
Gide должен предоставлять максимально удобный механизм управления "внешними" объектами. Набор таких объектов определяет набор решаемых задач, т.е. некий контекст того или иного скрипта.<br />
<br />
=== Модель языка ===<br />
<br />
Основополагающим понятием в модели Gide является объект.<br />
<br />
'''Объект''' - это некоторая абстрактная сущность, для которой определены свойства (поля) и операции (методы). Свойства объекта также являются (именованными) ссылками на объекты. И набор свойств, и набор операций - это динамические наборы, т.е. наличие того или иного свойства/операции определяется в момент выполнения, а не в момент компиляции. Gide позволяет оперировать ссылками на объекты. То есть единственный существующий в нем тип данных - это ссылка на объект. Ссылка может либо указывать на объект, либо быть равной 0.<br />
<br />
Набор методов объекта определяется его классом.<br />
<br />
'''Класс''' - это набор функций, каждая из которых имеет доступ к объекту, для которого она была вызвана. У класса может быть один или несколько '''родительских''' классов: при поиске метода, он ищется сначала в классе объекта, а затем - рекурсивно - во всех родительских классах, в порядке их перечисления.<br />
<br />
'''Функция''', в свою очередь - это некоторый набор операторов, выполняющих определенные действия и составляющие '''тело функции'''. Функция может быть методом какого-либо класса, либо отдельной функцией, не являющейся методом. Функция может принимать некоторое количество параметров и возвращать результат (объект).<br />
<br />
Набор классов, предоставляемых скрипту, формирует так называемый контекст исполнения, т.е. по сути стандартную библиотеку языка.<br />
<br />
Эта библиотека, для тех или иных вариантов использования, может включать в себя:<br />
* объекты стандартных типов данных:<br />
** целые числа,<br />
** вещественные числа,<br />
** строки,<br />
** идентификаторы объектов,<br />
** указатели,<br />
** массивы;<br />
* стандартные операции для этих объектов:<br />
** арифметические операции,<br />
** операции сравнения,<br />
** математические функции,<br />
** и пр.<br />
<br />
== Синтаксис языка gide ==<br />
<br />
Синтаксис языка Gide максимально упрощен - с рассчетом на то, что поверх него будут писаться реализации языков более высокого уровня.<br />
<br />
Каждая непустая строка исходного кода gide - это некоторая инструкция, записанная в следующей форме:<br />
ключевое_слово аргумент1, аргумент2, ..., аргументN<br />
<br />
Некоторые инструкции допустимы только за пределами тела функции, некоторые, наоборот, допустимы только внутри тела функции.<br />
<br />
Аргументы для инструкции - это произвольные текстовые строки. Если значение аргумента содержит управляющие символы (пробел, табуляция, перевод строки, запятая, символы # или ") - его необходимо заключить в двойные кавычки. Внутри таких кавычек также допустимы escape-последовательности \r, \n, \t, \" и \\.<br />
<br />
'''ЗАМЕЧАНИЕ''': вставка произвольных символов \xNN на данный момент не поддерживается, но запланирована на будущее.<br />
<br />
Весь текст от символа # и до конца строки - комментарий, он игнорируется.<br />
<br />
Внутри тела функции также возможно вставлять т.н. '''метки'''. Метка - это некоторая уникальная в пределах данной функции ссылка на инструкцию. Метка выглядит следующим образом:<br />
имя_метки:<br />
<br />
Она может быть записана как на отдельной строке, так и на строке с инструкцией.<br />
<br />
=== Подключение внешних модулей ===<br />
<br />
Для использования классов и функций, объявленных во внешних модулях (любого типа), необходимо их подключить, используя инструкцию '''use'''. Она записывается следующим образом:<br />
<br />
use ''адрес''<br />
<br />
Для неуправляемых модулей, адрес - это просто имя такого модуля.<br />
<br />
Для управляемых модулей, адрес - это префикс 'gbc:' плюс URL-адрес потока модуля. Например, "gbc:diss:/lib/module.gbc".<br />
<br />
Зависимости модулей - рекурсивные. Другими словами, если модуль B подключил модуль A, а модуль C подключил модуль B, то из модуля C будут доступны объявления модуля A.<br />
<br />
=== Объявление классов ===<br />
<br />
Для того, чтобы объявить класс, нужно написать следующую инструкцию:<br />
<br />
class ''имя_класса''<br />
<br />
Имя класса - это произвольная строка. Данная инструкция объявляет класс, в котором еще нет ни одного метода.<br />
<br />
Инструкция<br />
<br />
inherit ''имя_класса'', ''имя_родителя''<br />
<br />
добавляет класс ''имя_родителя'' в качестве родительского класса. Она должна быть записана после объявления класса. В этой инструкции, и класс ''имя_класса'', и класс ''имя_родителя'' могут быть объявлены во внешнем модуле.<br />
<br />
Существует также понятие родителя по-умолчанию. Они могут быть заданы только в неуправляемых модулях. Такие родители добавляются автоматически, если не указаны другие родительские классы.<br />
<br />
=== Объявление глобальных переменных ===<br />
<br />
Для того, чтобы объявить глобальную переменную - т.е. переменную, доступную из любой функции - нужно написать инструкцию<br />
<br />
global ''имя_переменной''<br />
<br />
В качестве имени переменной, опять же, может выступать любая строка.<br />
<br />
Глобальные переменные доступны только в пределах данного модуля. Для получения их значений из других модулей, следует использовать функции.<br />
<br />
По-умолчанию все глобальные переменные инициализируются нулем.<br />
<br />
=== Объявление функции ===<br />
<br />
Функция объявляется следующим образом:<br />
<br />
function ''имя_функции'', ''аргумент1'', ''аргумент2'', ..., ''аргументN''<br />
''тело функции''<br />
end<br />
<br />
Имя функции - это произвольная текстовая строка. Если имя имеет вид<br />
''имя_класса'':''имя_метода''<br />
, метод ''имя_метода'' добавляется в класс ''имя_класса'' (этот класс должен быть предварительно объявлен, но не обязательно в текущем модуле). В противном случае, объявляется функция, а не метод.<br />
<br />
Аргументы - это формальные параметры функции, они доступны в теле как локальные переменные.<br />
<br />
Список аргументов в какой-то мере условен. При вызове функции всегда можно указывать произвольное число параметров, это нигде не проверяется. Если фактических параметров меньше, чем формальных - оставшиеся аргументы будут инициализированы нулем. Если фактических больше - некоторые из них не будут доступны напрямую, однако стандартная библиотека может предоставлять функции, открывающие к ним доступ.<br />
<br />
Методы класса могут быть публичными (public), защищенными (protected) или частными (private). Публичные методы можно вызывать без ограничений, защищенные - только из данного класса и его потомков, частные - только из данного класса. Эти проверки делаются во время исполнения.<br />
<br />
По-умолчанию, метод является публичным. Для объявления защищенных и частных методов, нужно заменить ключевое слово function в объявлении метода на '''func_protected''' и '''func_private''' соответственно.<br />
<br />
Если метод/функция уже был объявлен ранее, он переобъявляется - т.е. данная реализация перекрывает предыдущую. Вызвать предыдущую реализацию можно при помощи оператора '''recall''' (см. ниже).<br />
<br />
Функция со специальным именем '''@@module_init''' является конструктором модуля; она вызывается в момент загрузки данного модуля. В ней, в частности, можно инициализировать глобальные переменные модуля.<br />
<br />
Метод '''@@init''' - конструктор класса. Он вызывается в момент создания объекта данного класса. Порядок вызова конструкторов таков: сначала рекурсивно вызываются конструкторы родительских классов в порядке объявления родителей, затем - конструктор данного класса. В конструкторе, в частности, можно инициализировать поля объекта.<br />
<br />
=== Специальные переменные ===<br />
<br />
Существует 6 зарезервированных имен переменных:<br />
* '''0''' (число ноль) - означает отсутствие объекта;<br />
* '''@true''' - указывает на специальный объект "истина". Этот объект не имеет полей и методов; он бывает полезен для написания логических конструкций;<br />
* '''@false''' - то же, что и 0;<br />
* '''@this''' - внутри функции-метода, указывает на текущий объект; внутри обычной функции равен 0;<br />
* '''@result''' - результат последней вызванной функции или оператора '''access''';<br />
* '''@exception''' - объект исключения, которое требуется обработать.<br />
<br />
=== Операторы функции ===<br />
<br />
'''Операторы''' - это управляющие инструкции, составляющие тело функции.<br />
<br />
Поскольку единственный существующий тип переменной - это указатель на объект, значительная часть операторов не требуется. Все операторы реализуются на уровне самих объектов в виде методов.<br />
<br />
Существуют следующие операторы:<br />
<br />
new ''переменная'', ''имя_класса'', ''строка''<br />
<br />
: создает новый объект класса ''имя_класса'' и записывает результат в указанную переменную. Если переменная была объявлена ранее - результат записывается в нее; если нет - создается новая '''локальная''' переменная (т.е. переменная, доступная только в пределах данной функции). Третий параметр - это некоторые данные для инициализации объекта. Они имеют значение только для некоторых классов в неуправляемых модулях. Кроме того, любой класс может быть инициализирован пустой строкой.<br />
<br />
bless ''переменная'', ''имя_класса''<br />
<br />
: эквивалент <tt>new</tt> без последнего параметра.<br />
<br />
mov ''переменная'', ''источник''<br />
<br />
: копирует в переменную ''переменная'' ссылку на существующий объект ''источник'' (аналогично, если переменная не была объявлена ранее - создается новая локальная переменная). Важно подчеркнуть, что при этой операции не создается нового объекта; две переменные будут указывать на один и тот же объект.<br />
<br />
goto ''имя_метки''<br />
<br />
: осуществляет безусловный переход к оператору, помеченному меткой ''имя_метки''.<br />
<br />
if ''переменная'', ''имя_метки''<br />
<br />
: осуществляет условный переход к оператору ''имя_метки'': если управляющая переменная (первый параметр) имеет отличное от нуля значение, осуществляется переход; в противном случае этот оператор игнорируется и выполнение продолжается со следующей инструкции.<br />
<br />
call ''переменная'', ''имя_функции'', ''аргумент1'', ..., ''аргументN''<br />
<br />
: если ''переменная'' - '''0''', вызывает функцию ''имя_функции''; в противном случае вызывает метод объекта, на который ссылается переменная. Как уже говорилось ранее, количество аргументов может быть любым вне зависимости от количества формальных параметров. Результат выполнения функции записывается в специальную переменную '''@result'''.<br />
<br />
: ''имя_функции'' может быть задано в форме ''имя_класса'':''имя_метода''. В этом случае осуществляется вызов метода конкретного класса, а не текущего (''имя_класса'' должно либо совпадать с классом объекта, либо быть его родительским классом). Это позволяет вызывать методы родительских классов, несмотря на перекрытие в дочерних классах.<br />
<br />
return ''переменная''<br />
<br />
: осуществляет выход из текущей функции с возвратом объекта, на который ссылается ''переменная''. Если выполнение функции доходит до конца, функция возвращает 0.<br />
<br />
access ''переменная'', ''имя_поля''<br />
<br />
: получает значение поля ''имя_поля'' объекта, на который ссылается ''переменная''. Значение записывается в специальную переменную '''@result'''. Если указанного поля в классе нет, в '''@result''' записывается 0.<br />
<br />
mutate ''переменная'', ''имя_поля'', ''значение''<br />
<br />
: записывает объект, на который ссылается ''значение'', в поле ''имя_поля'' объекта, на который ссылается ''переменная''. Если такого поля ранее не существовало, оно создается.<br />
<br />
recall<br />
<br />
: вызывает замещенную функцию с теми же параметрами, которые были переданы данной функции; осуществляет выход из текущей функции с результатом, который вернула замещенная функция.<br />
<br />
upgrade ''переменная'', ''имя_класса''<br />
<br />
: расширяет объект, на который ссылается ''переменная'', до класса ''имя_класса''. Это делается путем вызова конструкторов классов, которые в дереве иерархии находятся между классом переменной и указанным классом. Очевидно, класс объекта должен быть родительским для класса ''имя_класса''. После выполнения данной инструкции, класс объекта станет равным ''имя_класса'', т.е. для него будут доступны все методы этого класса.<br />
<br />
use_locals ''имя_функции''<br />
<br />
: открывает данной функции доступ к локальным переменным функции ''имя_функции''. Этот оператор может быть указан только в самом начале тела функции и только единожды. ''имя_функции'' записывается как при объявлении: для обычной функции - просто ее имя, для метода класса - имя класса и имя метода, разделенные двоеточием. Для того, чтобы локальные переменные реально стали доступны, функция ''имя_функции'' должна вызвать текущую функцию специальным образом.<br />
<br />
: '''ПРИМЕЧАНИЕ''': такой специальный вызов на данный момент невозможно сделать напрямую. Класс [[Closure]] стандартной библиотеки позволяет создать указатель на метод, при вызове которого (через этот указатель) происходит требуемый тип вызова. В будущем планируется добавить оператор вызова '''ccall''', выполняющий эту операцию.<br />
<br />
: данная инструкция требуется для реализации [http://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%BC%D1%8B%D0%BA%D0%B0%D0%BD%D0%B8%D0%B5_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29 замыканий].<br />
<br />
Кроме перечисленных, существуют еще операторы для работы с исключениями; они рассмотрены в следующем подразделе.<br />
<br />
=== Обработка исключений ===<br />
<br />
'''Исключения''' - это специальный механизм, предназначенный для передачи информации об ошибках. Он заключается в следующем.<br />
<br />
Любая функция может бросить исключение. Само исключение - это некоторый объект произвольного класса. Когда такое происходит, виртуальная машина начинает искать ближайший обработчик исключений. Если сама функция, бросившая исключение, может его обработать - исполнение передается соответствующей инструкции. В противном случае, начинается раскрутка стека вызовов до первой функции, которая может обработать исключение. Если ни одна функция этого сделать не может, выполнение программы прекращается, а вызвавшему модулю сообщается о необработанном исключении.<br />
<br />
В функции может быть установлено несколько обработчиков исключений, которые формируют стек. Обработчик - это некоторая метка в пределах функции. Когда функции требуется обработать исключение, управление передается оператору, на который ссылается эта метка, а сама эта метка извлекается из стека обработчиков. Другими словами, в функции может быть установлено несколько обработчиков, которые, при необходимости, будут вызываться в порядке, обратном их установке. Если же возникает исключение, а в функции не установлено ни одного обработчика - происходит выход из функции, а исключение передается функции, которая вызвала данную функцию, и т.д.<br />
<br />
Для работы с исключением существуют следующие четыре оператора:<br />
<br />
throw ''переменная''<br />
<br />
: кидает исключение, передав в качестве параметра объект, на который ссылается ''переменная''.<br />
<br />
except_push ''имя_метки''<br />
<br />
: добавляет метку ''имя_метки'' в стек обработчиков исключений для данной функции. Когда возникает исключение, управление передается указанной метке, а объект исключения записывается в специальную переменную '''@exception'''.<br />
<br />
except_pop<br />
<br />
: изымает из стека обработчиков метку, находящуюся на вершине стека.<br />
<br />
except_clr<br />
<br />
: очищает стек обработчиков исключений для данной функции.<br />
<br />
Кроме оператора '''throw''', исключения могут кидаться неуправляемыми модулями. Кроме того, существует механизм, при помощи которого ошибки, возникающие на уровне самой виртуальной машины, передаются в стандартную библиотеку, которая кидает исключение.<br />
<br />
== Формат байт-кода gide ==<br />
<br />
<font color="red">'''ПРИМЕЧАНИЕ:''' в данном разделе приведена устаревшая информация, которая не соответствует действительности. Раздел оставлен для ознакомления с общей концепцией хранения байт-кода. Через некоторое время он будет переписан.</font><br />
<br />
Байт-код gide представляет собой популярный BER-формат: весь файл разбит на секции, каждая из которых начинается с описания ее типа и размера. <br />
<br />
Следует сразу оговориться, что все числа в gide представляются в формате big endian (т.е. наименее значимый байт впереди). <br />
<br />
Заголовок BER-секции представляет собой последовательность кода секции (1 байт) и ее размер в BER-формате.<br />
<br />
Все целые числа в байт-коде, если это не оговорено специально, представляются в BER-формате: для записи числа требуется столько байт, сколько нужно; в каждом байте записывается 7 битов числа. Если самый старший бит равен 1, требуется прочитать/записать следующий байт. Если старший бит равен 0 - это последний байт, описывающий число.<br />
<br />
=== Секция имен ===<br />
<br />
Тип секции - код символа N.<br />
<br />
Для большей компактности кода используется секция имен. В ней последовательно записываются все текстовые строки (имена переменных/функций, данные конструкторов и пр.), которые встречаются в скрипте. Одинаковые текстовые строки записываются только один раз. Таким образом, каждая текстовая строка получает свой уникальный номер, и в байт-коде вместо строк используются эти номера.<br />
<br />
Поскольку в скрипте часто встречаются одинаковые строки (например, работа с одной и той же переменной), это сильно уменьшает размер кода.<br />
<br />
Все строки записываются следующим образом:<br />
* длина строки (в BER формате, см. выше);<br />
* текст.<br />
<br />
Нумерация строк начинается не с нуля, а с 16. Меньшие номера зарезервированы для стандартных имен:<br />
* Переменная @null (она же 0) имеет номер 0;<br />
* Переменная @this имеет номер 1;<br />
* Переменная @false имеет номер 2;<br />
* Переменная @true имеет номер 3.<br />
<br />
В случае обращения к полю объекта, в байт-код записывается все составное имя объекта (вместе с разделяющими точками). Виртуальная машина сама разбирает составные имена.<br />
<br />
=== Секция экспорта ===<br />
<br />
Тип секции - ASCII-код символа E<br />
<br />
Данная секция предназначена для упрощения навигации по секции кода.<br />
<br />
Для каждой функции, описанной в модуле, записывается:<br />
* индекс имени функции;<br />
* смещение функции относительно начала секции кода.<br />
<br />
=== Секция кода ===<br />
<br />
Тип секции кода - ASCII-код символа C.<br />
<br />
В эту секцию записываются тела функций Gide.<br />
<br />
Для каждой функции записывается:<br />
* индекс имени функции (он должен быть смещен от начала секции кода на количество байт, указанных в секции экспорта);<br />
* количество аргументов функции;<br />
* индексы имен аргументов функции;<br />
* размер блока операторов;<br />
* для каждого оператора:<br />
** индекс имени оператора;<br />
** количество передаваемых параметров функции (только для оператора call);<br />
** индексы имен всех параметров.<br />
<br />
Индексы операторов:<br />
* Простые операторы имеют номера от 1 до 9 в порядке их перечисления в п.2.1;<br />
* Оператор создания объекта имеет номер 10;<br />
* Оператор throw имеет номер 13;<br />
* Оператор except_push имеет номер 16;<br />
* Оператор except_pop имеет номер 17;<br />
* Оператор создания переменной пользовательского класса имеет номер 11;<br />
* Оператор расширения наследования имеет номер 15;<br />
* Оператор use имеет номер 12;<br />
* Оператор external номер 14;<br />
<br />
=== Секция импорта ===<br />
<br />
Тип секции импорта - ASCII-код символа I<br />
<br />
В эту секцию записываются подключаемые операторами use и external библиотеки.<br />
<br />
Для каждой библиотеки записывается:<br />
* число 1 в случае оператора external, 0 - в случае use;<br />
* индекс пути к библиотеке.<br />
<br />
=== Секция наследования классов ===<br />
<br />
Тип секции - код символа H.<br />
<br />
В эту секцию записывается статическая таблица наследований классов.<br />
<br />
Для каждого оператора inherit записывается:<br />
* индекс наследующего класса;<br />
* индекс наследуемого класса.<br />
<br />
=== Секция номеров строк ===<br />
<br />
Тип секции - код символа D.<br />
<br />
Эта секция не является обязательной. В ней записываются соответствия операторов и номеров строк в исходных файлах, на которых указан соответствующий оператор.<br />
<br />
В секцию записывается массив следующих структур:<br />
* индекс имени файла;<br />
* размер блока функций;<br />
* для каждой функции:<br />
** индекс имени функции;<br />
** размер блока операторов;<br />
** для каждого оператора:<br />
*** номер оператора в теле функции;<br />
*** номер строки кода, на которой объявлен оператор.</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BB%D0%B0%D1%82%D1%84%D0%BE%D1%80%D0%BC%D1%8B_GideОписание платформы Gide2010-08-29T18:12:14Z<p>Raw mat: Правки 144.140.53.252 (обсуждение) откачены к версии Lester</p>
<hr />
<div>== Введение ==<br />
<br />
=== Роль скриптов в проекте ===<br />
<br />
Многие объекты виртуального мира обладают некоторым специфичным поведением. Например, при нажатии на кнопку зажигается лампочка, а для открытия некоторых дверей требуется ключ (пароль). Для того, чтобы задать специфичное поведение объекту, предусмотрено два способа:<br />
<br />
# написание плагина к физическому движку Диптауна, расширяющего стандартное дерево классов объектов;<br />
# написание скрипта на специализированном языке программирования.<br />
<br />
Еще одна важная область применения скриптов - т.н. процедурные модели и текстуры. Суть их заключается в следующем.<br />
<br />
Как известно, классически текстура задается в виде массива чисел, каждое из которых определяет цвет соответствующей точки. Даже при достаточно хорошем сжатии, текстуры имеют большой размер и их очень "дорого" передавать по сети. Альтернативой являются процедурные текстуры. Процедурная текстура задается некоторой программой (скриптом), которая рисует изображение. При этом по сети передается относительно маленькая программа, которая затем исполняется на машине пользователя, а полученное изображение используется в качестве текстуры. <br />
<br />
Аналогично, процедурная модель - это скрипт, создающий трехмерную модель. <br />
<br />
Итак, скрипты в проекте используются, в основном, в трех направлениях: <br />
* задание поведения объектов; <br />
* создание процедурных моделей; <br />
* создание процедурных текстур.<br />
<br />
=== Терминология ===<br />
<br />
В этом документе будут использованы следующие термины: <br />
* Gide (гайд) - единый низкоуровневый язык; <br />
* байт-код gide - бинарный код, получаемый после компиляции кода gide; <br />
* виртуальная машина gide - программа, исполняющая байт-код. <br />
<br />
=== Основные концепции ===<br />
<br />
Для различных задач удобны различные языки и различные наборы функций. Поэтому, во избежание повторного написания низкоуровневых библиотек, а также для удобства программирования скриптовых языков, было решено разработать единый низкоуровневый язык - gide. Код любого другого языка компилируется в код gide, затем компилятор gide создает платформенно-независимый байт-код, который исполняется виртуальной машиной gide. Механизм компиляции и исполнения кода изображен на рис. 1. <br />
<br />
[[Image:gidebc-1.gif|thumb|Рис. 1. Механизм компиляции и исполнения кода]]<br />
<br />
Преимущества такого подхода очевидны: <br />
* использование библиотек одного языка другими; <br />
* единая виртуальная машина для всех языков. <br />
<br />
Язык gide берет за основу модульную архитектуру. Модули gide - это динамически подключаемые библиотеки, которые бывают двух типов: <br />
# библиотеки, представляющие собой байт-код gide ('''управляемые''' модули);<br />
# "обычные" динамически-подключаемые библиотеки ('''неуправляемые''' модули).<br />
<br />
Любые из этих библиотек можно подключать как статически, так и динамически.<br />
Область задач gide:<br />
* расчет процедурных текстур, объектов и, возможно, звуков;<br />
* управление поведением объектов в виртуальном пространстве; <br />
* управление объектами интерфейса на сервере интерфейса.<br />
<br />
Gide должен предоставлять максимально удобный механизм управления "внешними" объектами. Набор таких объектов определяет набор решаемых задач, т.е. некий контекст того или иного скрипта.<br />
<br />
=== Модель языка ===<br />
<br />
Основополагающим понятием в модели Gide является объект.<br />
<br />
'''Объект''' - это некоторая абстрактная сущность, для которой определены свойства (поля) и операции (методы). Свойства объекта также являются (именованными) ссылками на объекты. И набор свойств, и набор операций - это динамические наборы, т.е. наличие того или иного свойства/операции определяется в момент выполнения, а не в момент компиляции. Gide позволяет оперировать ссылками на объекты. То есть единственный существующий в нем тип данных - это ссылка на объект. Ссылка может либо указывать на объект, либо быть равной 0.<br />
<br />
Набор методов объекта определяется его классом.<br />
<br />
'''Класс''' - это набор функций, каждая из которых имеет доступ к объекту, для которого она была вызвана. У класса может быть один или несколько '''родительских''' классов: при поиске метода, он ищется сначала в классе объекта, а затем - рекурсивно - во всех родительских классах, в порядке их перечисления.<br />
<br />
'''Функция''', в свою очередь - это некоторый набор операторов, выполняющих определенные действия и составляющие '''тело функции'''. Функция может быть методом какого-либо класса, либо отдельной функцией, не являющейся методом. Функция может принимать некоторое количество параметров и возвращать результат (объект).<br />
<br />
Набор классов, предоставляемых скрипту, формирует так называемый контекст исполнения, т.е. по сути стандартную библиотеку языка.<br />
<br />
Эта библиотека, для тех или иных вариантов использования, может включать в себя:<br />
* объекты стандартных типов данных:<br />
** целые числа,<br />
** вещественные числа,<br />
** строки,<br />
** идентификаторы объектов,<br />
** указатели,<br />
** массивы;<br />
* стандартные операции для этих объектов:<br />
** арифметические операции,<br />
** операции сравнения,<br />
** математические функции,<br />
** и пр.<br />
<br />
== Синтаксис языка gide ==<br />
<br />
Синтаксис языка Gide максимально упрощен - с рассчетом на то, что поверх него будут писаться реализации языков более высокого уровня.<br />
<br />
Каждая непустая строка исходного кода gide - это некоторая инструкция, записанная в следующей форме:<br />
ключевое_слово аргумент1, аргумент2, ..., аргументN<br />
<br />
Некоторые инструкции допустимы только за пределами тела функции, некоторые, наоборот, допустимы только внутри тела функции.<br />
<br />
Аргументы для инструкции - это произвольные текстовые строки. Если значение аргумента содержит управляющие символы (пробел, табуляция, перевод строки, запятая, символы # или ") - его необходимо заключить в двойные кавычки. Внутри таких кавычек также допустимы escape-последовательности \r, \n, \t, \" и \\.<br />
<br />
'''ЗАМЕЧАНИЕ''': вставка произвольных символов \xNN на данный момент не поддерживается, но запланирована на будущее.<br />
<br />
Весь текст от символа # и до конца строки - комментарий, он игнорируется.<br />
<br />
Внутри тела функции также возможно вставлять т.н. '''метки'''. Метка - это некоторая уникальная в пределах данной функции ссылка на инструкцию. Метка выглядит следующим образом:<br />
имя_метки:<br />
<br />
Она может быть записана как на отдельной строке, так и на строке с инструкцией.<br />
<br />
=== Подключение внешних модулей ===<br />
<br />
Для использования классов и функций, объявленных во внешних модулях (любого типа), необходимо их подключить, используя инструкцию '''use'''. Она записывается следующим образом:<br />
<br />
use ''адрес''<br />
<br />
Для неуправляемых модулей, адрес - это просто имя такого модуля.<br />
<br />
Для управляемых модулей, адрес - это префикс 'gbc:' плюс URL-адрес потока модуля. Например, "gbc:diss:/lib/module.gbc".<br />
<br />
Зависимости модулей - рекурсивные. Другими словами, если модуль B подключил модуль A, а модуль C подключил модуль B, то из модуля C будут доступны объявления модуля A.<br />
<br />
=== Объявление классов ===<br />
<br />
Для того, чтобы объявить класс, нужно написать следующую инструкцию:<br />
<br />
class ''имя_класса''<br />
<br />
Имя класса - это произвольная строка. Данная инструкция объявляет класс, в котором еще нет ни одного метода.<br />
<br />
Инструкция<br />
<br />
inherit ''имя_класса'', ''имя_родителя''<br />
<br />
добавляет класс ''имя_родителя'' в качестве родительского класса. Она должна быть записана после объявления класса. В этой инструкции, и класс ''имя_класса'', и класс ''имя_родителя'' могут быть объявлены во внешнем модуле.<br />
<br />
Существует также понятие родителя по-умолчанию. Они могут быть заданы только в неуправляемых модулях. Такие родители добавляются автоматически, если не указаны другие родительские классы.<br />
<br />
=== Объявление глобальных переменных ===<br />
<br />
Для того, чтобы объявить глобальную переменную - т.е. переменную, доступную из любой функции - нужно написать инструкцию<br />
<br />
global ''имя_переменной''<br />
<br />
В качестве имени переменной, опять же, может выступать любая строка.<br />
<br />
Глобальные переменные доступны только в пределах данного модуля. Для получения их значений из других модулей, следует использовать функции.<br />
<br />
По-умолчанию все глобальные переменные инициализируются нулем.<br />
<br />
=== Объявление функции ===<br />
<br />
Функция объявляется следующим образом:<br />
<br />
function ''имя_функции'', ''аргумент1'', ''аргумент2'', ..., ''аргументN''<br />
''тело функции''<br />
end<br />
<br />
Имя функции - это произвольная текстовая строка. Если имя имеет вид<br />
''имя_класса'':''имя_метода''<br />
, метод ''имя_метода'' добавляется в класс ''имя_класса'' (этот класс должен быть предварительно объявлен, но не обязательно в текущем модуле). В противном случае, объявляется функция, а не метод.<br />
<br />
Аргументы - это формальные параметры функции, они доступны в теле как локальные переменные.<br />
<br />
Список аргументов в какой-то мере условен. При вызове функции всегда можно указывать произвольное число параметров, это нигде не проверяется. Если фактических параметров меньше, чем формальных - оставшиеся аргументы будут инициализированы нулем. Если фактических больше - некоторые из них не будут доступны напрямую, однако стандартная библиотека может предоставлять функции, открывающие к ним доступ.<br />
<br />
Методы класса могут быть публичными (public), защищенными (protected) или частными (private). Публичные методы можно вызывать без ограничений, защищенные - только из данного класса и его потомков, частные - только из данного класса. Эти проверки делаются во время исполнения.<br />
<br />
По-умолчанию, метод является публичным. Для объявления защищенных и частных методов, нужно заменить ключевое слово function в объявлении метода на '''func_protected''' и '''func_private''' соответственно.<br />
<br />
Если метод/функция уже был объявлен ранее, он переобъявляется - т.е. данная реализация перекрывает предыдущую. Вызвать предыдущую реализацию можно при помощи оператора '''recall''' (см. ниже).<br />
<br />
Функция со специальным именем '''@@module_init''' является конструктором модуля; она вызывается в момент загрузки данного модуля. В ней, в частности, можно инициализировать глобальные переменные модуля.<br />
<br />
Метод '''@@init''' - конструктор класса. Он вызывается в момент создания объекта данного класса. Порядок вызова конструкторов таков: сначала рекурсивно вызываются конструкторы родительских классов в порядке объявления родителей, затем - конструктор данного класса. В конструкторе, в частности, можно инициализировать поля объекта.<br />
<br />
=== Специальные переменные ===<br />
<br />
Существует 6 зарезервированных имен переменных:<br />
* '''0''' (число ноль) - означает отсутствие объекта;<br />
* '''@true''' - указывает на специальный объект "истина". Этот объект не имеет полей и методов; он бывает полезен для написания логических конструкций;<br />
* '''@false''' - то же, что и 0;<br />
* '''@this''' - внутри функции-метода, указывает на текущий объект; внутри обычной функции равен 0;<br />
* '''@result''' - результат последней вызванной функции или оператора '''access''';<br />
* '''@exception''' - объект исключения, которое требуется обработать.<br />
<br />
=== Операторы функции ===<br />
<br />
'''Операторы''' - это управляющие инструкции, составляющие тело функции.<br />
<br />
Поскольку единственный существующий тип переменной - это указатель на объект, значительная часть операторов не требуется. Все операторы реализуются на уровне самих объектов в виде методов.<br />
<br />
Существуют следующие операторы:<br />
<br />
new ''переменная'', ''имя_класса'', ''строка''<br />
<br />
: создает новый объект класса ''имя_класса'' и записывает результат в указанную переменную. Если переменная была объявлена ранее - результат записывается в нее; если нет - создается новая '''локальная''' переменная (т.е. переменная, доступная только в пределах данной функции). Третий параметр - это некоторые данные для инициализации объекта. Они имеют значение только для некоторых классов в неуправляемых модулях. Кроме того, любой класс может быть инициализирован пустой строкой.<br />
<br />
bless ''переменная'', ''имя_класса''<br />
<br />
: эквивалент <tt>new</tt> без последнего параметра.<br />
<br />
mov ''переменная'', ''источник''<br />
<br />
: копирует в переменную ''переменная'' ссылку на существующий объект ''источник'' (аналогично, если переменная не была объявлена ранее - создается новая локальная переменная). Важно подчеркнуть, что при этой операции не создается нового объекта; две переменные будут указывать на один и тот же объект.<br />
<br />
goto ''имя_метки''<br />
<br />
: осуществляет безусловный переход к оператору, помеченному меткой ''имя_метки''.<br />
<br />
if ''переменная'', ''имя_метки''<br />
<br />
: осуществляет условный переход к оператору ''имя_метки'': если управляющая переменная (первый параметр) имеет отличное от нуля значение, осуществляется переход; в противном случае этот оператор игнорируется и выполнение продолжается со следующей инструкции.<br />
<br />
call ''переменная'', ''имя_функции'', ''аргумент1'', ..., ''аргументN''<br />
<br />
: если ''переменная'' - '''0''', вызывает функцию ''имя_функции''; в противном случае вызывает метод объекта, на который ссылается переменная. Как уже говорилось ранее, количество аргументов может быть любым вне зависимости от количества формальных параметров. Результат выполнения функции записывается в специальную переменную '''@result'''.<br />
<br />
: ''имя_функции'' может быть задано в форме ''имя_класса'':''имя_метода''. В этом случае осуществляется вызов метода конкретного класса, а не текущего (''имя_класса'' должно либо совпадать с классом объекта, либо быть его родительским классом). Это позволяет вызывать методы родительских классов, несмотря на перекрытие в дочерних классах.<br />
<br />
return ''переменная''<br />
<br />
: осуществляет выход из текущей функции с возвратом объекта, на который ссылается ''переменная''. Если выполнение функции доходит до конца, функция возвращает 0.<br />
<br />
access ''переменная'', ''имя_поля''<br />
<br />
: получает значение поля ''имя_поля'' объекта, на который ссылается ''переменная''. Значение записывается в специальную переменную '''@result'''. Если указанного поля в классе нет, в '''@result''' записывается 0.<br />
<br />
mutate ''переменная'', ''имя_поля'', ''значение''<br />
<br />
: записывает объект, на который ссылается ''значение'', в поле ''имя_поля'' объекта, на который ссылается ''переменная''. Если такого поля ранее не существовало, оно создается.<br />
<br />
recall<br />
<br />
: вызывает замещенную функцию с теми же параметрами, которые были переданы данной функции; осуществляет выход из текущей функции с результатом, который вернула замещенная функция.<br />
<br />
upgrade ''переменная'', ''имя_класса''<br />
<br />
: расширяет объект, на который ссылается ''переменная'', до класса ''имя_класса''. Это делается путем вызова конструкторов классов, которые в дереве иерархии находятся между классом переменной и указанным классом. Очевидно, класс объекта должен быть родительским для класса ''имя_класса''. После выполнения данной инструкции, класс объекта станет равным ''имя_класса'', т.е. для него будут доступны все методы этого класса.<br />
<br />
use_locals ''имя_функции''<br />
<br />
: открывает данной функции доступ к локальным переменным функции ''имя_функции''. Этот оператор может быть указан только в самом начале тела функции и только единожды. ''имя_функции'' записывается как при объявлении: для обычной функции - просто ее имя, для метода класса - имя класса и имя метода, разделенные двоеточием. Для того, чтобы локальные переменные реально стали доступны, функция ''имя_функции'' должна вызвать текущую функцию специальным образом.<br />
<br />
: '''ПРИМЕЧАНИЕ''': такой специальный вызов на данный момент невозможно сделать напрямую. Класс [[Closure]] стандартной библиотеки позволяет создать указатель на метод, при вызове которого (через этот указатель) происходит требуемый тип вызова. В будущем планируется добавить оператор вызова '''ccall''', выполняющий эту операцию.<br />
<br />
: данная инструкция требуется для реализации [http://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%BC%D1%8B%D0%BA%D0%B0%D0%BD%D0%B8%D0%B5_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29 замыканий].<br />
<br />
Кроме перечисленных, существуют еще операторы для работы с исключениями; они рассмотрены в следующем подразделе.<br />
<br />
=== Обработка исключений ===<br />
<br />
'''Исключения''' - это специальный механизм, предназначенный для передачи информации об ошибках. Он заключается в следующем.<br />
<br />
Любая функция может бросить исключение. Само исключение - это некоторый объект произвольного класса. Когда такое происходит, виртуальная машина начинает искать ближайший обработчик исключений. Если сама функция, бросившая исключение, может его обработать - исполнение передается соответствующей инструкции. В противном случае, начинается раскрутка стека вызовов до первой функции, которая может обработать исключение. Если ни одна функция этого сделать не может, выполнение программы прекращается, а вызвавшему модулю сообщается о необработанном исключении.<br />
<br />
В функции может быть установлено несколько обработчиков исключений, которые формируют стек. Обработчик - это некоторая метка в пределах функции. Когда функции требуется обработать исключение, управление передается оператору, на который ссылается эта метка, а сама эта метка извлекается из стека обработчиков. Другими словами, в функции может быть установлено несколько обработчиков, которые, при необходимости, будут вызываться в порядке, обратном их установке. Если же возникает исключение, а в функции не установлено ни одного обработчика - происходит выход из функции, а исключение передается функции, которая вызвала данную функцию, и т.д.<br />
<br />
Для работы с исключением существуют следующие четыре оператора:<br />
<br />
throw ''переменная''<br />
<br />
: кидает исключение, передав в качестве параметра объект, на который ссылается ''переменная''.<br />
<br />
except_push ''имя_метки''<br />
<br />
: добавляет метку ''имя_метки'' в стек обработчиков исключений для данной функции. Когда возникает исключение, управление передается указанной метке, а объект исключения записывается в специальную переменную '''@exception'''.<br />
<br />
except_pop<br />
<br />
: изымает из стека обработчиков метку, находящуюся на вершине стека.<br />
<br />
except_clr<br />
<br />
: очищает стек обработчиков исключений для данной функции.<br />
<br />
Кроме оператора '''throw''', исключения могут кидаться неуправляемыми модулями. Кроме того, существует механизм, при помощи которого ошибки, возникающие на уровне самой виртуальной машины, передаются в стандартную библиотеку, которая кидает исключение.<br />
<br />
== Формат байт-кода gide ==<br />
<br />
<font color="red">'''ПРИМЕЧАНИЕ:''' в данном разделе приведена устаревшая информация, которая не соответствует действительности. Раздел оставлен для ознакомления с общей концепцией хранения байт-кода. Через некоторое время он будет переписан.</font><br />
<br />
Байт-код gide представляет собой популярный BER-формат: весь файл разбит на секции, каждая из которых начинается с описания ее типа и размера. <br />
<br />
Следует сразу оговориться, что все числа в gide представляются в формате big endian (т.е. наименее значимый байт впереди). <br />
<br />
Заголовок BER-секции представляет собой последовательность кода секции (1 байт) и ее размер в BER-формате.<br />
<br />
Все целые числа в байт-коде, если это не оговорено специально, представляются в BER-формате: для записи числа требуется столько байт, сколько нужно; в каждом байте записывается 7 битов числа. Если самый старший бит равен 1, требуется прочитать/записать следующий байт. Если старший бит равен 0 - это последний байт, описывающий число.<br />
<br />
=== Секция имен ===<br />
<br />
Тип секции - код символа N.<br />
<br />
Для большей компактности кода используется секция имен. В ней последовательно записываются все текстовые строки (имена переменных/функций, данные конструкторов и пр.), которые встречаются в скрипте. Одинаковые текстовые строки записываются только один раз. Таким образом, каждая текстовая строка получает свой уникальный номер, и в байт-коде вместо строк используются эти номера.<br />
<br />
Поскольку в скрипте часто встречаются одинаковые строки (например, работа с одной и той же переменной), это сильно уменьшает размер кода.<br />
<br />
Все строки записываются следующим образом:<br />
* длина строки (в BER формате, см. выше);<br />
* текст.<br />
<br />
Нумерация строк начинается не с нуля, а с 16. Меньшие номера зарезервированы для стандартных имен:<br />
* Переменная @null (она же 0) имеет номер 0;<br />
* Переменная @this имеет номер 1;<br />
* Переменная @false имеет номер 2;<br />
* Переменная @true имеет номер 3.<br />
<br />
В случае обращения к полю объекта, в байт-код записывается все составное имя объекта (вместе с разделяющими точками). Виртуальная машина сама разбирает составные имена.<br />
<br />
=== Секция экспорта ===<br />
<br />
Тип секции - ASCII-код символа E<br />
<br />
Данная секция предназначена для упрощения навигации по секции кода.<br />
<br />
Для каждой функции, описанной в модуле, записывается:<br />
* индекс имени функции;<br />
* смещение функции относительно начала секции кода.<br />
<br />
=== Секция кода ===<br />
<br />
Тип секции кода - ASCII-код символа C.<br />
<br />
В эту секцию записываются тела функций Gide.<br />
<br />
Для каждой функции записывается:<br />
* индекс имени функции (он должен быть смещен от начала секции кода на количество байт, указанных в секции экспорта);<br />
* количество аргументов функции;<br />
* индексы имен аргументов функции;<br />
* размер блока операторов;<br />
* для каждого оператора:<br />
** индекс имени оператора;<br />
** количество передаваемых параметров функции (только для оператора call);<br />
** индексы имен всех параметров.<br />
<br />
Индексы операторов:<br />
* Простые операторы имеют номера от 1 до 9 в порядке их перечисления в п.2.1;<br />
* Оператор создания объекта имеет номер 10;<br />
* Оператор throw имеет номер 13;<br />
* Оператор except_push имеет номер 16;<br />
* Оператор except_pop имеет номер 17;<br />
* Оператор создания переменной пользовательского класса имеет номер 11;<br />
* Оператор расширения наследования имеет номер 15;<br />
* Оператор use имеет номер 12;<br />
* Оператор external номер 14;<br />
<br />
=== Секция импорта ===<br />
<br />
Тип секции импорта - ASCII-код символа I<br />
<br />
В эту секцию записываются подключаемые операторами use и external библиотеки.<br />
<br />
Для каждой библиотеки записывается:<br />
* число 1 в случае оператора external, 0 - в случае use;<br />
* индекс пути к библиотеке.<br />
<br />
=== Секция наследования классов ===<br />
<br />
Тип секции - код символа H.<br />
<br />
В эту секцию записывается статическая таблица наследований классов.<br />
<br />
Для каждого оператора inherit записывается:<br />
* индекс наследующего класса;<br />
* индекс наследуемого класса.<br />
<br />
=== Секция номеров строк ===<br />
<br />
Тип секции - код символа D.<br />
<br />
Эта секция не является обязательной. В ней записываются соответствия операторов и номеров строк в исходных файлах, на которых указан соответствующий оператор.<br />
<br />
В секцию записывается массив следующих структур:<br />
* индекс имени файла;<br />
* размер блока функций;<br />
* для каждой функции:<br />
** индекс имени функции;<br />
** размер блока операторов;<br />
** для каждого оператора:<br />
*** номер оператора в теле функции;<br />
*** номер строки кода, на которой объявлен оператор.</div>Raw mathttp://man.deeptown.org/index.php/DirectoryDirectory2010-07-09T15:10:57Z<p>Raw mat: </p>
<hr />
<div>Класс <tt>Directory</tt> представляет одну директорию файловой системы [[DISS]]. <br />
<br />
'''Родители''': [[Node (DISS)|Node]] <br><br />
'''Методы''': <tt>[[#items|items]]</tt><br />
<br />
__NOTOC__<br />
<br />
== items ==<br />
<br />
'''Возвращает''': <tt>[[array]]<[[Node (DISS)|Node]]></tt><br />
<br />
Метод возвращает содержимое директории в виде массива потомков <tt>[[Node (DISS)|Node]]</tt>.</div>Raw mathttp://man.deeptown.org/index.php/%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B8Задачи2010-05-06T04:56:47Z<p>Raw mat: </p>
<hr />
<div>В данном разделе мы публикуем текущий список задач Deeptown SDK. Большая часть задач — это разработка тех или иных модулей/программ на языке [[K++]]. Некоторые задачи предполагают реализацию на C++.<br />
<br />
Данный раздел будет пополняться со временем. Особо важные изменения будут отмечаться в [[Рассылка|рассылке]].<br />
<br />
== Информация ==<br />
<br />
=== Для кого этот раздел ===<br />
<br />
Браться за выполнение задач может абсолютно любой желающий. В том числе и Вы, дорогой читатель ;)<br />
<br />
Для простых задач потребуются только базовые навыки программирования и знание [[K++]], который [[K++|подробно описан]] на этом сайте. Категории сложности задач обозначены в их описаниях.<br />
<br />
Однако будьте готовы к тому, что мы не примем некачественный код. Мы следим за качеством нашей системы. Перед тем, как приступать, пожалуйста, ознакомьтесь с [[Правила оформления кода|правилами оформления кода]].<br />
<br />
=== Оформление данного раздела ===<br />
<br />
В данном разделе задачи разбиты на несколько категорий. Каждая задача — это подраздел в своей категории. Простые задачи описываются прямо в тексте; для более сложных даются ссылки на отдельные страницы описаний. Новые задачи всегда добавляются наверх своего раздела.<br />
<br />
Для каждой задачи приводится следующая служебная информация:<br />
* '''сложность''' — уровень сложности задачи: простая, средняя, сложная<br />
* '''технологии''' — список технологий (языков, библиотек, etc), которые потребуются для выполнения задачи<br />
* '''приоритет''' — степень важности задачи: низкий, средний, высокий<br />
* '''автор''' — подпись автора задачи<br />
* '''примечания''' — технические примечания автора<br />
* '''исполнитель''' — контактная информация исполнителя<br />
* '''срок''' — срок завершения, обозначенный исполнителем<br />
<br />
=== Порядок выполнения задач ===<br />
<br />
Прежде всего, выберите задачу, за которую Вы хотели бы взяться. Выбирать следует только из тех задач, для которых не указан исполнитель. Предпочтительнее брать задачи с более высоким приоритетом, но это не обязательно: прежде всего выбирайте то, что Вам будет интересно делать.<br />
<br />
После того как выбор сделан, вставьте информацию об исполнителе и о сроке.<br />
<br />
В информации об исполнителе должно указываться какое-нибудь средство связи с Вами: e-mail, ICQ или jabber. Вы можете написать свои контактные данные в "мою страницу" вики, а в графу "исполнитель" просто поставить подпись.<br />
<br />
Срок введен для того, чтобы отсекать "призраков". Представьте что кто-то взял задачу и пропал на долгое время. С одной стороны передавать эту задачу кому-то другому нельзя, потому что она уже занята; с другой — работа стоит.<br />
<br />
Срок — это не строгое поле. Если Вы активно работаете над задачей, но не успеваете в срок — Вы всегда можете его отодвинуть. Главное для нас то, что процесс идет.<br />
<br />
В поле "срок" следует вписать предполагаемую дату завершения. Максимальный срок, который Вы можете установить — это текущая дата плюс 2 недели, если иное не указано в примечаниях к задаче.<br />
<br />
Процедура обнаружения "призраков" такова. Когда срок исполнения выходит, мы связываемся с Вами по указанным контактным данным, и узнаем статус задачи/договариваемся о дальнейшем. Если связаться не удается в течение недели — мы убираем информацию об исполнителе, открывая таким образом задачу для других.<br />
<br />
=== Порядок приема задач ===<br />
<br />
На данный момент у нас нет (публичного) централизованного обменника исходными кодами. Поэтому, высылайте свои труды в архиве на адрес developers (гав) deeptown.org. Либо Вы можете выложить их куда-нибудь в интернет, и выслать ссылку по этому адресу.<br />
<br />
=== Условия лицензирования ===<br />
<br />
Мы можем принять Ваш код только в том случае, если Вы передаете его нам на условиях свободной лицензии. Пожалуйста, вложите текст лицензии в архив с кодом. Мы не будем принимать архивы без текстов лицензии, поскольку это может грозить нам судебными исками (исключение — только для наших разработчиков, которые подписали с нами договор).<br />
<br />
Предпочтительной является [http://www.opensource.org/licenses/mit-license.php лицензия MIT], но Вы можете взять другую свободную лицензию или даже написать свою. Однако имейте ввиду: мы ответственно подходим к лицензированию кода, поэтому без внимания мы это не оставим.<br />
<br />
Со своей стороны обещаем соблюдать условия лицензии, либо не принимать Ваш код, если нас она не устроит.<br />
<br />
=== Графические обозначения ===<br />
<br />
Для облегчения навигации по заданиям применено цветовое кодирование сложности поставленной задачи и ее востребованности:<br />
<br />
Категории сложности:<br />
: <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span><br />
: <span style="color: white; background-color: #008000;">· низкая ·</span><br />
: <span style="color: white; background-color: #FFCC00;">· средняя ·</span><br />
: <span style="color: white; background-color: #FF8000;">· выше среднего ·</span><br />
: <span style="color: white; background-color: #DD0000;">· высокая ·</span><br />
<br />
<br />
Категории востребованности (приоритет):<br />
: <span style="color: white; background-color: #A0A0A0;">· может подождать ·</span><br />
: <span style="color: white; background-color: #808080;">· низкий ·</span><br />
: <span style="color: white; background-color: #FFCC00;">· средний ·</span><br />
: <span style="color: white; background-color: #FF8000;">· выше среднего ·</span><br />
: <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
<br />
== Операторы DSH ==<br />
<br />
По сути это команды, но играют роль структурных элементов шеллового языка программирования. Необходимо реализовать команды: true, false, test, if, switch, for, foreach.<br />
<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': <br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 07:38, 18 июня 2008 (EDT)<br />
* '''Примечания''': <br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
== Базовые команды оболочки DSH ==<br />
<br />
Ниже приведены команды, которые требуется реализовать для нормального функционирования командной оболочки Диптауна (DSH). Дабы не изобретать велосипед, было решено делать команды максимально похожими на их UNIX аналоги. Однако, следует помнить, что при реализации команд, надо учитывать специфику диптауна и ориентироваться на максимальное юзабилити а не стопроцентное соответствие UNIX. Короче говоря, не надо стараться реализовать полный клон команды, со всеми "наворотами" — достаточно только базовых возможностей, которые гарантированно понадобятся и будут полезны.<br />
<br />
'''Примечание''': Все без исключения команды должны поддерживать следующие ключи:<br />
;'''--version''': Вывод информации о версии программы и ее авторе.<br />
;'''--help''': Вывод краткой справки по использованию команды. Может выводиться так же при отсутствии какого либо ввода со стороны пользователя (вызов команды без параметров), если это не противоречит логике работы команды.<br />
;'''--''': Индикатор окончания списка параметров. Если в строке параметров встречается данный символ, это означает что дальнейшая информация уже не является параметрами. Например, команды <br />
::<tt>deep$ ls -l</tt><br />
и<br />
::<tt>deep$ ls -- -l</tt><br />
:имеют различный смысл. В первом случае вызывается расширенный список файлов текущей директории, тогда как во втором делается попытка отобразить содержимое директории с именем "-l".<br />
;'''-''': Отдельный дефиз может использоваться всесто имени файла для указания того, что данные требуется получать из стандартного устройства ввода. А вывод — соответственно направлять в устройство вывода. Конкретное поведение зависит от используемой команды. Таким образом, следующие команды являются эквивалентами:<br />
::<tt>deep$ cat </tt><br />
::<tt>deep$ cat - </tt><br />
<br />
<br />
=== <s>ls</s> === <br />
<br />
Команда ls (от англ. ''list'' — список) выводит в терминал содержимое некоторой директории. Формат списка, а так же исследуемая директория, задются с помощью соответствующих ключей. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]], [[регулярные выражения]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 07:38, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=ls здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано<br />
<br />
=== <s>cat</s> === <br />
<br />
Команда cat выводит в терминал содержимое некоторого файла.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]], [[регулярные выражения]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 07:38, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=ls здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано<br />
<br />
<br />
=== <s>cp</s> === <br />
<br />
Команда cp (от англ. ''copy'' — копировать) производит копирование содержимого указанного каталога в новый каталог. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=cp здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': реализовано<br />
<br />
<br />
=== <s>mv</s> === <br />
<br />
Команда mv (от англ. ''move'' — переместить) производит перемещение содержимого указанного каталога в новый каталог, либо переименовывает файлы (каталоги). <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=mv здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано. <br />
<br />
<br />
=== <s>rm</s> === <br />
<br />
Команда rm (от англ. ''remove'' — удалить) удаляет файл, либо каталог. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=rm здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': реализовано<br />
<br />
<br />
=== <s>touch</s> === <br />
<br />
Команда touch (от англ. ''touch'' — потрогать) создает пустой файл с указанным именем, либо изменяет время доступа существующего файла. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=touch здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': реализовано<br />
<br />
<br />
=== <s>mkdir</s> === <br />
<br />
Команда mkdir (от англ. ''make directory'') создает директорию с указанным именем. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=mkdir здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано<br />
<br />
<br />
=== <s>grep</s> === <br />
<br />
Команда grep производит фильтрацию своего входного потока и выдает результат в выходной поток. Следует иметь в виду, что реализация PCRE (используемая в К++) синтаксически отличается от канонической UNIX. Основная задача — выборка интересующих строк из входного потока.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FF8000;">· выше среднего ·</span><br />
<br />
* '''Технологии''': [[DISS]], [[регулярные выражения]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=grep здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано<br />
<br />
<br />
=== sed === <br />
<br />
Команда sed (от англ. ''stream editor'' — редактор потоков) производит фильтрацию своего входного потока и выдает результат в выходной поток. Следует иметь в виду, что реализация PCRE (используемая в К++) синтаксически отличается от канонической UNIX. Основная задача — преобразование данных по шаблону.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[DISS]], [[регулярные выражения]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=sed здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== <s>wc</s> === <br />
<br />
Команда wc (от англ. ''word counter'' — счетчик слов) производит подсчет количества структурных элементов в своем входном потоке и выдает результат в выходной поток. В качестве таких элементов могут выступать символы (ключ <tt>-c</tt>), строки (ключ <tt>-l</tt>) и др. Таким образом, простейший способ подсчета количества строк в файле может выглядеть так:<br />
:<tt>deep$ cat myfile | wc -l</tt><br />
<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[DISS]], [[регулярные выражения]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=wc здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано<br />
<br />
<br />
=== <s>nl</s> === <br />
<br />
Команда nl (от англ. ''numbered lines'' — пронумерованные строки) записывает в стандартный поток вывода данные из своего стандартного потока ввода, предворяя их номерами строк. Может использоваться для формирования листингов программ, составления отчетов и др.<br />
<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': <br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=nl здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано<br />
<br />
<br />
=== sort === <br />
<br />
Команда sort производит сортировку данных из своего входного потока и выдает результат в выходной поток. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=sort здесь].<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== <s>head</s> === <br />
<br />
Команда head возвращает первые N строк из своего входного потока. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=head здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано. <br />
<br />
<br />
=== <s>tail</s> === <br />
<br />
Команда head возвращает последние N строк из своего входного потока. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=tail здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано. <br />
<br />
<br />
=== <s>more</s> === <br />
<br />
Команда more выводит данные из своего входного потока порциями по N строк. Используется для постепенного отображения содержимого буфера в консоль (так, чтобы пользователь успел все прочитать).<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #00FF00;">· раз плюнуть ·</span> '''Приоритет''': <span style="color: white; background-color: #DD0000;">· высокий ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=more здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Реализовано<br />
<br />
<br />
=== mount === <br />
<br />
Команда mount используется для привязки носителей данных к файловой системе Диптауна. Принципы ее работы несколько отличаются от UNIX аналога, поэтому документация не дается. Для получения информации, необходимо [[Обратная связь|связаться с разработчиками]].<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FF8000;">· выше среднего ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''':<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== chmod === <br />
<br />
Команда chmod используется для изменения прав доступа к файлам в файловой системе Диптауна. Принципы ее работы несколько отличаются от UNIX аналога, поэтому документация не дается. Для получения информации, необходимо [[Обратная связь|связаться с разработчиками]].<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FF8000;">· выше среднего ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''':<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': НЕ РЕАЛИЗУЕМО НА ДАННОМ ЭТАПЕ.<br />
<br />
<br />
=== chown === <br />
<br />
Команда chown используется для изменения владельца и группы файлов в файловой системе Диптауна. <br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FFCC00;">· средний ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=chown здесь].<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': НЕ РЕАЛИЗУЕМО НА ДАННОМ ЭТАПЕ.<br />
<br />
<br />
=== chattr === <br />
<br />
Команда chattr используется для изменения атрибутов файлов (в основном метаинформации) в файловой системе Диптауна. Принципы ее работы несколько отличаются от UNIX аналога, поэтому документация не дается. Для получения информации, необходимо [[Обратная связь|связаться с разработчиками]].<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FFCC00;">· средний ·</span><br />
<br />
* '''Технологии''': [[DISS]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': <br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': НЕ РЕАЛИЗУЕМО НА ДАННОМ ЭТАПЕ.<br />
<br />
<br />
=== su === <br />
<br />
Команда su (от англ. ''switch user'' — переключить пользователя) используется для запуска подоболочки от имени другого пользователя.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[UserDB]], [[DSH]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=su здесь].<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== sudo === <br />
<br />
Команда sudo позволяет выполнить команду от имени другого пользователя.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #808080;">· низкий ·</span><br />
<br />
* '''Технологии''': [[UserDB]], [[DSH]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=sudo здесь].<br />
* '''Исполнитель''': <span style="color: #808080;">не назначен (вакансия)</span><br />
* '''Срок''': <span style="color: #808080;">не установлен</span><br />
<br />
<br />
=== <s>passwd</s> === <br />
<br />
Команда passwd позволяет сменить пароль пользователя.<br />
<br />
* '''Сложность''': <span style="color: white; background-color: #008000;">· низкая ·</span> '''Приоритет''': <span style="color: white; background-color: #FFCC00;">· средний ·</span><br />
<br />
* '''Технологии''': [[UserDB]], [[DSH]]<br />
* '''Автор''': --[[Участник:Korvin|Korvin]] 08:24, 18 июня 2008 (EDT)<br />
* '''Примечания''': Описание UNIX аналога команды можно найти [http://www.opennet.ru/man.shtml?topic=passwd здесь].<br />
* '''Исполнитель''': [http://ding.deeptown.org Deeptown DING project]<br />
* '''Срок''': Частично реализовано. <br />
<br />
== Стандартный интерфейс SQL ==<br />
== Универсальный HTTP клиент ==<br />
== Универсальный HTTP сервер ==<br />
== Репозиторий медиаданных ==</div>Raw mathttp://man.deeptown.org/index.php/%D0%9F%D0%BB%D0%B0%D1%82%D1%84%D0%BE%D1%80%D0%BC%D0%B0_GideПлатформа Gide2010-05-06T04:56:20Z<p>Raw mat: Правки 216.246.16.236 (обсуждение) откачены к версии Raw mat</p>
<hr />
<div>''страница требует правки''<br />
<br />
* [[Описание платформы Gide]]</div>Raw mathttp://man.deeptown.org/index.php/Deptown_Programming_Contest_(DPC)Deptown Programming Contest (DPC)2010-05-06T04:55:47Z<p>Raw mat: </p>
<hr />
<div>Здравствуй Гость! Ты попал на страничку программерского соревнования в рамках проекта Диптаун =)<br />
<br />
DPC это соревнование, участие в котором принимают роботы, находящиеся в виртуальном пространстве диптауна. Управляет роботом программа, написанная на языке [[K++]].<br />
<br />
Идея соревнования была заимствована из проекта [http://robocode.sourceforge.net/ Robocode] и адаптирована под текущие реалии и специфику платформы Диптаун. <br />
<br />
<br />
== Для чего это нужно? ==<br />
<br />
Если говорить откровенно, то мы еще сами не знаем зачем все это и что из этого может получиться :)<br />
<br />
Но на самом деле, у DPC целей много. Во-первых, популяризация самого проекта Диптаун и наглядная демонстрация возможностей платформы Диптауна. Во-вторых, это отличный способ изучить язык [[K++]] в игровой обстановке, а так же попробовать себя в решении нечетких задач и умения находить нестандартные решения. В-третьих, лучший способ отладить некую систему, это начать активно ее использовать. Заодно, мы сможем лучше представить, какие функции более всего востребованы разработчиками, что использовать удобно, а что требуется изменить, ну и так далее.<br />
<br />
Наконец, это просто забавно и жутко интересно :)<br />
<br />
== Основная идея ==<br />
<br />
Есть некая арена, в пространстве которой существуют боевые роботы. Каждый робот принадлежит одному из участников. На каждом роботе крутится программа управления этим роботом. Цель -- победить соперников и остаться в живых. Третьего не дано :) <br />
<br />
== Правила соревнований ==<br />
<br />
DPC это в первую очередь соревнование мозгов, а потом уже случайностей и прочих факторов. Поэтому, влияние случайностей должно быть сведено к минимуму. Однако убрать их совсем не получится, опять же в силу специфики платформы, да это и не нужно. Впрочем, нельзя исключать и элемент зрелищности, где случай может сыграть свою роль. Поэтому на данный момент у меня есть несколько вариантов игры :)<br />
<br />
На данный момент, я их представляю следующим образом: <br />
<br />
== Вариант первый: Sumo Challenge ==<br />
<br />
[[Изображение:DPC.png|right]]<br />
В этом варианте соревнований самые простые правила и роботы. Однако это не значит что победить в нем легко. Наоборот, мне кажется что это лучший из вариантов, поскольку все зависит от тактики робота и качества программы. <br />
<br />
=== Арена ===<br />
<br />
Арена -- это пространство для соревнования. Арена зависит от типа проводимого соревнования. В данном случае это площадка без ограждений, размерами скажем 10 на 10 метров. Сама площадка может висеть над пустотой или просто являться возвыщением. <br />
<br />
В определенных местах на нее помещаются два робота соперника. По сингалу роботы начинают движение. Основная задача -- вытолкнуть соперника за пределы площадки, самому оставшись на ней. Роботы полностью идентичны, они состоят из одинаковых компонентов и имеют одинаковые параметры мощности и скорости. Единственное отличие в программе управления. Более умная программа должна учитывать положение соперника и нападать, в то же время стараясь не подставляться под удар.<br />
<br />
<br />
=== Роботы ===<br />
<br />
Sumo это простейший вид соревнований, соответственно и роботы должны быть простые :) На данный момент планируется 2 вида роботов: четырех колесные и трехколесные. Первый имеет два рулевых колсеса являющиеся так же ведущими (машина с передним приводом), второй -- два ведущих и одно рулевое (трайк).<br />
<br />
Трехколесный робот более маневренный, однако и менее устойчивый. Четырехколесный более устойчивый, но управлять им сложнее. <br />
<br />
<br />
=== Управление ===<br />
<br />
Независимо от конструкции, все роботы обладают следущим набором систем:<br />
* Приводы колес (моторы). Можно задавать скорость вращения и угол поворота (для ведущих колес).<br />
* Сенсор краев -- с его помощью можнно определять положение робота на площадке<br />
* Радар -- служит для слежения за роботом противника. Дает информацию о его скорости и направлении движения.<br />
<br />
Дополнительно, сам робот знает скорость своего движения и свое положение в пространстве арены.<br />
<br />
<br />
=== Стратегии ===<br />
<br />
Постараюсь привести пример нескольких стратегий поведения робота, а также возможные контр стратегии:<br />
<br />
==== Берсерк ====<br />
<br />
Наверное самая ломовая стратегия :) Просто фиксируем положение противника и гоним в него, в надежде что так мы его скинем с площадки. <br />
<br />
* Плюсы: Простота кода :)<br />
* Минусы: Если противник не дурак, то уже после первой игры можно придумать как уворачиваться от такого. Не забывайте, что если берсерк достаточно разгонится, то затормозить он уже не успеет и просто улетит за пределы площадки, в результате проиграет :)<br />
<br />
<br />
==== Трус ====<br />
<br />
Стратегия, обратная "берсерку". Стараемся всячески избегать противника и находиться от него подальше. <br />
<br />
* Плюсы: выиграть сложно, но зато сам не улетишь. При определенных условиях, грамотно написанный "трус", будет выигрывать у "берсерка", поскольку последний сам улетит :)<br />
* Минусы: если ваш противник не берсерк, то выиграть будет практически невозможно<br />
<br />
<br />
==== Тормоз ====<br />
<br />
Данная стратегия направлена на максимальное затягивание времени ведущее к ничьей. Робот может нарезать круги по площадке, одновременно сочетая в себе черты труса.<br />
<br />
* Плюсы: если ваша цель -- ничья, то можно подумать об этом варианте :)<br />
* Минусы: умрем раньше, чем два "тормоза" на арене завершат бой <br />
<br />
<br />
==== А что дальше? ====<br />
<br />
Важно понимать, что вышеописанные стратегии являются лишь типовыми шаблонами. Настоящий боевой алгоритм должен уметь сочетать в себе всех трех персонажей. Например, он может вести себя как трус, уворачиваясь от ударов агрессивного соперника, в то же время если сложилась такая ситуация что соперник оказался на самом краю площадки, такой робот может перейти в режим наступления и тем самым выиграть.<br />
<br />
В общем случае стратегий можно придумать бесконечное количество :) Все ограничивается лишь вашей фантазией, временем и желанием :)<br />
<br />
<br />
=== Очки ===<br />
<br />
За каждую победу начисляется одно очко, за поражение ноль.<br />
<br />
Для предотвращения затягивания боя, вводится трехминутное ограничение на длину боя. Если по истечении этого времени оба робота остались на площадке, бой завершается вничью. Для большей статистической значимости возможно проведение нескольких боев, после чего победившим считается соперник, набравший большее количество очков.<br />
<br />
<br />
== Вариант второй -- классическая Robocode ==<br />
<br />
Здесь на первое место выходит зрелищность :) Действие происходит на поле, на котором может находиться сразу несколько соперников. Каждый соперник представлен танком, имеющим пушку и радар. По условиям, танки так же идентичны. <br />
<br />
На поле боя могут находиться вспомогательные элементы вроде стен или возвышений. Возвышения позволяют стрелять дальше, если въехать на них, стены помогают защищаться от вражеских выстрелов.<br />
<br />
Возможных вариантов стратегий здесь еще больше чем в Sumo. Описывать их все не хватит времени, тем более что по этой теме есть масса материалов на [http://robocode.sourceforge.net/ официальном сайте].<br />
<br />
<br />
== Текущее положение дел ==<br />
<br />
--[[Участник:Velaar|Velaar]] 02:56, 18 мая 2009 (NOVST)<br />
Всех желающих принять участие в реализации проекта просим в [http://ding.deeptown.org Deeptown DING project].<br />
<br />
Как только перейдем к этому проекту, появится соотвествующая новость и запись здесь.</div>Raw mathttp://man.deeptown.org/index.php/%D0%A2%D1%83%D1%82%D0%BE%D1%80%D0%B8%D0%B0%D0%BB%D1%8B_%D0%B8_HOWTOТуториалы и HOWTO2010-05-06T04:53:24Z<p>Raw mat: Правки 213.3.45.238 (обсуждение) откачены к версии Raw mat</p>
<hr />
<div>На этой странице размещаются документы, затрагивающие практические вопросы использования платформы диптауна, а так же пошаговые руководства, позволяющие научиться выполнять разнообразные операции, вроде импортирования новых моделей, написания скриптов и т. д.<br />
<br />
Если у вас есть идея или вы хотите поделиться своим опытом -- пожалуйста пишите свои HOWTO и помогите новичкам. Так же, не стоит забывать про [http://forum.deeptown.org форум], на котором можно вести обсуждение статей и задавать вопросы.<br />
<br />
При написании собственной статьи, указывайте пожалуйста свое авторство. Это можно сделать автоматически, с помощью последовательности символов '''<nowiki>--~~~~</nowiki>'''<br />
<br />
<br />
== Туториалы ==<br />
<br />
Здесь должны размещаться статьи, которые подразумевают изучение некоторой темы на некотором конкретном примере. Читатель, повторяя операции должен в итоге прийти к тем же результатам что и автор.<br />
<br />
* [[С чего начать|Первый запуск и советы новичкам]] --[[Участник:Korvin|Korvin]] 12:34, 27 апреля 2008 (EDT)<br />
* [[Тюнинг машины для достижения лучших результатов]] --[[Участник:Korvin|Korvin]] 12:34, 29 апреля 2008 (EDT)<br />
* [[Работа с флеш объектами на примере скрипта bonnet.kpp]] --[[Участник:Korvin|Korvin]] 12:34, 29 апреля 2008 (EDT)<br />
<br />
== HOWTO ==<br />
<br />
HOWTO ориентируются на более общий случай и описывают действия в более общем смысле, однако все равно должны отражать четкую последовательность действий, которая должна выполняться пользователем для достижения результата.<br />
<br />
* [[Импортирование моделей в Диптаун]] --[[Участник:Korvin|Korvin]] 12:34, 29 апреля 2008 (EDT)<br />
* [[Добавление текстуры для земли и ящиков]] --[[Участник:Stmf|Stmf]] 22:42, 21 июля 2008 (EDT)</div>Raw mathttp://man.deeptown.org/index.php/%D0%9E_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5О проекте2010-05-06T04:53:16Z<p>Raw mat: Правки 74.202.241.69 (обсуждение) откачены к версии Raw mat</p>
<hr />
<div>== Для кого эта wiki ==<br />
<br />
Наконец-то, после долгих лет разработки, проект [http://www.deeptown.org/ Deeptown] вплотную подобрался к этапу открытого тестирования. До этого момента разработка проекта велась в основном "вглубь" — мы проектировали основные технологии и писали реализацию их основ — т.е. скелет проекта. Теперь же, когда ядро платформы почти готово и, хоть и со скрипом, но работает, можно приступать к разработке приложений на его основе.<br />
<br />
Горизонты тут открываются достаточно широкие: начиная от драйверов хранения данных DISS и поддержки различных протоколов передачи, и заканчивая всевозможными приложениями на платформе Gide (пользовательский интерфейс, управление объектами, процедурные текстуры и многое другое).<br />
<br />
Очевидно, что нашей относительно небольшой команде разработчиков не справиться со всем этим в одиночку. К тому же, платформа для того и разрабатывалась, чтобы в дальнейшем разрабатывать на ее основе приложения; мы хотим предоставить эту возможность как можно раньше.<br />
<br />
Поэтому мы запустили ряд сервисов для тех, кто хотел бы участвовать в расширении платформы Диптауна, и в создании приложений на ее основе. Эта wiki является отправной точкой для доступа к этим сервисам. На ней также будет вестись разработка документации к различным технологиям и средствам разработки в рамках платформы Диптауна.<br />
<br />
Остальные сервисы включают в себя:<br />
* [[Deeptown SDK|Deeptown Software Development Kit]] — набор программ и утилит для разработки системных и пользовательских приложений;<br />
* [[Багтрекер]] — для отчетов об ошибках;<br />
* [[Рассылка|Почтовая рассылка]] — включает в себя два списка рассылки: рассылку новостей и рассылку для обратной связи с разработчиками.<br />
<br />
== Участие ==<br />
<br />
Проект в целом и эта wiki в частности, подразумевают коллективную работу сообщества. Любой желающий может помочь проекту либо непосредственным участием в делах группы разработчиков, либо как вебмастер - установив на своем сайте ссылку на наш проект, либо косвенно, как посетитель этого сайта. Как вы уже могли заметить, на сайте и на wiki представлено большое количество материалов. Однако мы не в силах всецело контролировать качество публикуемых текстов, хотя и стремимся к этому.<br />
<br />
Вы можете помочь проекту даже не будучи программистом или дизайнером. Для этого достаточно просто быть грамотным человеком. Если на страницах этой wiki вы встретите неточности, орфографические или пунктуационные ошибки, или просто заметите дефиc там, где должно стоять тире — исправьте, пожалуйста, текст так, как вы считаете нужным. Это можно сделать с помощью вкладки "правка" вверху каждой страницы.<br />
<br />
Если вы сомневаетесь относительно верности вашего исправления, напишите об этом на странице обсуждения. Не забудьте только указать место в тексте, где, по вашему мнению, находится ошибка, и ваш вариант её исправления. Ссылка на страницу обсуждения также находится вверху каждой страницы.<br />
<br />
== Некоторые понятия == <br />
Наше название: '''Проект «Диптаун»'''<br><br />
По-английски: '''«Deeptown project»''' <br><br />
Слово «Deeptown» принято переводить как '''«Город-в-Глубине»''' <br><br />
<br><br />
Наш официальный сайт:<br> [http://www.deeptown.org www.deeptown.org]<br><br />
Наш официальный баннер:<br> [[Изображение:Deep_1_88x31.gif|Официальный баннер для сайта 'deeptown.org']]</div>Raw mathttp://man.deeptown.org/index.php/%D0%A0%D0%B0%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B0Рассылка2010-05-06T04:53:07Z<p>Raw mat: Правки 190.145.22.170 (обсуждение) откачены к версии Raw mat</p>
<hr />
<div>Для получения самой свежей информации о событиях, происходящих с Deeptown SDK, Вы можете подписаться на рассылку новостей.<br />
<br />
На данный момент эта рассылка предоставляется только по личному обращению. [[Обратная связь|Напишите нам]] письмо, в котором опишите, кто Вы и чем планируете заниматься - мы добавим Ваш адрес к рассылке. Это требуется для сбора информации о наших пользователях и формирования сообщества разработчиков для платформы Диптаун.<br />
<br />
Естественно, мы обещаем не распространять и тем более не продавать адреса наших подписчиков.</div>Raw mathttp://man.deeptown.org/index.php/%D0%A1_%D1%87%D0%B5%D0%B3%D0%BE_%D0%BD%D0%B0%D1%87%D0%B0%D1%82%D1%8CС чего начать2010-05-06T04:52:54Z<p>Raw mat: </p>
<hr />
<div>'''Внимание: Эта страница устарела. Некоторая информация может оказаться неактуальной'''<br />
<br />
== Где я? ==<br />
<br />
Если вы читаете эту страницу, то видимо уже установили себе Deeptown SDK и хотите что либо с ним сделать :)<br />
Если еще не установили, то вам сюда: [[Deeptown SDK]].<br />
<br />
Данная страница предназначена для того, чтобы дать самое общее представление о том, что же из себя представляет система Диптауна на данный момент и что с ней можно сделать интересного. Здесь мы постараемся (в свободной форме) описать возможные пути исследования и продемонстрировать некоторые вещи на примерах.<br />
<br />
То что здесь будет описано, по большей части преследует одну цель — дать человеку отправную точку и помочь сориентироваться в многообразии информации. Мы не в состоянии объяснить все, а можем лишь указать путь :) Поэтому — пробуйте, пытайтесь, изучайте систему и делитесь своим опытом на форуме :) Кроме того, эта wiki — так же к вашим услугам. Если вам есть что добавить или исправить — милости просим.<br />
<br />
== Что это? Как попасть в Диптаун? ==<br />
<br />
SDK это набор программных средств, включающий в себя саму платформу диптауна (aka "движок"), а так же набор разнообразных, сопутствующих программ, таких как компилятор языка К++. Короче говоря, это основа, на базе которой и будет строиться Диптаун. На данный момент, "того" Диптауна еще не существует. Точнее, существуют некоторые его части у наших архитекторов, но они еще не в публичном доступе. Так что, "попадать" пока еще некуда.<br />
<br />
Тем не менее, обычные пользователи могут попробовать использовать "все это" для того, чтобы оценить возможности системы и просто посмотреть "красивые картинки", которые скоро будут. Ниже будет описано, как заставить систему работать и посмотреть некоторые уже существующие демки.<br />
<br />
Люди, желающие поглубже изучить саму систему, найдут здесь полезную информацию о том, как совершаются типовые действия, вроде добавления объектов и их программирования. Более подробно это будет рассмотрено в соответствующей документации.<br />
<br />
== Первый запуск ==<br />
[[Изображение:Showto 1.png|thumb]]<br />
[[Изображение:Showto 2.png|thumb]]<br />
Для начала попробуем запустить все "как есть". Для этого нам понадобится собственно Deeptown SDK и архив с медиа файлами, который уже должен быть при вас, если вы внимательно читали инструкцию по установке. <br />
<br />
Попробуйте запустить файл deep.exe (если вы в windows) или /opt/deeptown/bin/deep в *nix. Будьте терпеливы, потому что в первый запуск система производит индексацию содержимого каталога Media (того самого) и создает в нем файл ".index", который используется для поиска файлов самой системой. При последующих запусках эта операция выполняться не будет (только в [[Индексация DISS|определенных случаях]]).<br />
<br />
Если все прошло успешно, то вы должны будете увидеть окошко рендеринга и, через некоторое время, простейшую демосцену, загружаемую по умолчанию. На данный момент это сцена "bonnet", содержащая в себе машину и кучку кубиков. Выглядеть это должно примерно как на картинках справа. <br />
<br />
Машиной можно ездить и сбивать кубики. Изначально они расположены справа и сзади от вас (конечно это все можно изменить!)<br />
<br />
=== Управление ===<br />
<br />
* Ctrl+Q — переключиться в режим курсора (управление окошком)<br />
* Двойное нажатие на заголовке внутреннего окна сворачивает его в заголовок (чтобы меньше места занимать)<br />
* W, S, A, D — движение камерой '''при зажатой кнопке Alt'''<br />
* Стрелки клавиатуры — управление машиной (попробуйте наехать на кубики)<br />
* Стрелка назад — тормозить (если едем назад — наоборот, давить кнопку вперед)<br />
* Кнопка "spawn object" интерфейса — создает в воздухе еще один кубик, где то неподалеку. Кубики создаются всегда случайным образом (и это тоже можно изменить!).<br />
* Кнопка "cleanup" — удаляет все созданные вами кубики (те которые были изначально — созданы другим путем, поэтому они останутся на месте)<br />
<br />
'''Примечания''': <br />
* Машина (а так же форма интерфейса) управляется скриптом /media/storage/media/scripts/bonnet.kpp<br />
* Переназначить клавиши управления можно редактируя файл /media/storage/etc/world/input.conf<br />
* Чтобы выйти из системы, надо либо нажать крестик на окне интерфейса, либо нажать Alt+Tab и закрыть окно рендеринга<br />
* Если вы обнаружили что то странное, либо программа попросту "совершила недопустимую операцию", то смело идите в багтрекер по адресу [http://bugs.deeptown.org http://bugs.deeptown.org] и оставляйте свое сообщение. <br />
* Есть еще и другие сцены, которые можно попробовать запустить. Для этого надо открыть файл /media/storage/etc/boot/services и раскомментировать одну из строчек client_wm:..., не забыв закомментировать строчку, где упоминается bonnet.scene!<br />
* Текущая версия модели ведет себя не очень хорошо, из за плохой настройки физических параметров. Для улучшения характеристик модели предусмотрен [[Тюнинг_машины_для_достижения_лучших_результатов|специальный туториал]]. Выполнив его вы научитесь править модели и получите хорошую модель машинки :)<br />
<br />
== Консоль ==<br />
<br />
Консоль Диптауна является стандартным средством взаимодействия с системой, которая позволяет (точнее будет позволять) всецело управлять системой и отдавать ей команды. На данный момент она реализована только в самых общих чертах (даже стандартных команд оболочки еще нет). В будущем она будет существенно расширена и дополнена. <br />
<br />
Подключение к консоли осуществляется по протоколу Telnet, любым из клиентов, поддерживающих этот протокол.<br />
<br />
Перед первым подключением необходимо создать пользователя Диптауна (не путать с пользователем операционной системы!). Для его создания необходимо запустить сценарий mkuser.pl, который находится в папке с медиаданными. У Вас будут запрошены имя пользователя, пароль, а также идентификаторы самого пользователя и его группы.<br />
<br />
Чтобы подключиться к консоли из ОС Windows надо:<br />
# зайти в меню Пуск<br />
# открыть диалог "выполнить" (или сразу нажать Win+R)<br />
# ввести туда <tt>telnet localhost 4830</tt> и нажать Enter<br />
<br />
Линуксоиды скорее всего и сами разберутся ;)<br />
<br />
Далее, можно попробовать запустить программы, имеющиеся по умолчанию. Например можно ввести команду /bin/hello.gbc и получить простенькое тестовое окно с известным сообщением. Если Вы сами написали консольную программу и откомпилировали её с помощью kpp, не забудьте добавить полученный gbc-файл в файловую систему, как описано [[Запуск_файла_kpp_из_консоли_Диптауна|здесь]]. Просьба не судить строго, ведь это не конечный продукт, а всего лишь тесты :)<br />
<br />
Если у Вас возникнет желание написать более толковое приложение — обращайтесь к нам и [http://forum.deeptown.org на форум].<br />
<br />
Выход из консоли осуществляется вводом команды exit.<br />
<br />
== Что еще можно посмотреть? ==<br />
<br />
Практически все "интересные" вещи сосредоточены в уже известной вам директории Media. Приведем здесь список основных поддиректорий с комментариями к ним.<br />
<br />
;Media/storage: Это корневая директория для внутренней ФС диптауна. Все внутренние пути указываются относительно нее. Скажем, если в файле материала указана строка texture <tt>/media/textures/grass.png</tt>, то это значит что реальный файл расположен по адресу <tt>Media/storage/media/textures/grass.png</tt>.<br />
;Media/storage/bin: Здесь будут располагаться исполняемые файлы скриптов.<br />
;Media/storage/boot: Директория системного загрузчика (ничего интересного).<br />
;Media/storage/etc: А здесь хранятся файлы настроек. Заглянуть рекомендуется настоятельно. По крайней мере поизучать. Попробуйте загрузить другую сцену вместо той что грузится по умолчанию. '''Примечание''': Все текстовые файлы в диптауне используют формат UNIX, когда строка переводится одним символом LF. Поэтому, в windows открывать их надо либо wordpad-ом (он правильно понимает переносы), либо другим редактором, поддерживающим такие файлы. Хорошо работает редактор в FAR Manager-е.<br />
;Media/storage/home: пока пустует. <br />
;Media/storage/media: Здесь тоже много чего интересного. Это основное хранилище различных медиа данных, имеющих отношение к собственно виртуальному пространству. Содержит такие поддиректории как:<br />
::* '''materials''' — файлы материалов OGRE. Определяют графические свойства поверхностей моделей, такие как текстуры, цвета, привязанные шейдеры и др.<br />
::* '''mein''' — медиафайлы для встроенного графического интерфейса, такие как скины и шрифты<br />
::* '''meshes''' — здесь хранятся меш файлы загружаемых моделей. Более подробно про это можно почитать в статье [[импорт моделей в диптаун]].<br />
::* '''models''' — а здесь хранятся сами модели. Модель это нечто большее, чем просто меш с натянутой текстурой. Файл модели описывает также понятия, относящиеся к другим представлениям объекта, а не только к графическому. Например, там указывается масса модели, момент ее инерции и много других параметров. Смотри так же [[понятие объекта в пространстве]].<br />
::* '''scenes''' — если кратко, то сцена — это совокупность объектов, расположенных в некоторой области виртуального пространства. В этой директории хранятся файлы локальных сцен.<br />
::* '''scripts''' — самое интересное :) Здесь расположены скрипты, задающие поведение отдельных объектов. Поскольку диптаун изначально создавался как интерактивная среда, то каждый объект, находящийся в виртуальном пространстве, может быть "живым" и "разумным". С объектом ассоциируется некоторый скрипт, который программирует его действия и управляет его поведением. Например, скрипт bonnet.kpp реализует интерфейс к той самой машине и позволяет пользователю (то есть нам с вами) управлять ей. Скрипт park.kpp создает интерфейс управления анимацией для сцены park, и сам создает эту анимацию. Все скрипты написаны на нашем языке [[K++]].<br />
::* '''shaders''' — хранилище для шейдеров. Думаю что это такое, объяснять не требуется.<br />
::* '''textures''' — ну и здесь наверное все понятно.<br />
<br />
== Что еще можно сделать? ==<br />
<br />
Это зависит, в первую очередь, от вашего желания и способностей, а так же от имеющихся навыков. <br />
<br />
=== Программистам ===<br />
<br />
Если вы знакомы с такими языками программирования как Паскаль, Си++ или Java, то вам не составит труда изучить, или по крайней мере разобраться, в языке [[K++]]. Он очень похож на эти языки.<br />
<br />
<s>Попробуйте например, модифицировать скрипт bonnet.kpp так, чтобы создаваемые кубики располагались не случайным образом а один над другим. Главное поставить высоту побольше, чтобы кубики не пытались телефрагить друг друга.</s><br />
<br />
=== Моделерам ===<br />
<br />
Если вы умеете моделировать объекты в 3D пакетах, таких как 3D Studio, Maya или Blender, и желаете создать свои модели, то вам стоит обратиться в раздел [[Разработка моделей]]. Там в скором времени появится информация о том, каких правил следует придерживаться при создании моделей, а так же детально будет расписан процесс конвертирования моделей в формат Диптауна.<br />
<br />
В настоящее время, мы испытываем большой дефицит людей этой профессии, так что если вас заинтересовал проект — смело обращайтесь [http://forum.deeptown.org на форум]. Мы постараемся оказать вам всестороннуюю помощь и поддержку.<br />
<br />
=== Всем ===<br />
<br />
Как вы понимаете, написание пространной и качественной документации — длительный и трудоемкий процесс, отнимающий наше время и силы. Несмотря на то, что мы стараемся поддерживать документацию на уровне, это не всегда возможно. Поэтому, если вы желаете поделиться собственным опытом в изучении Диптауна или написать HOWTO на определенную тему — пробуйте. Эта wiki к вашим услугам :) Собственно, она и создавалась для этих целей. В ближайшее время, мы постараемся написать серию HOWTO, затрагивающих основные вопросы, вроде скриптования объектов и создания собственных сцен. Ссылки на них будут размещаться [[Туториалы и HOWTO|здесь]]. Присоединяйтесь :)<br />
<br />
Вы можете помочь проекту, даже не будучи программистом или дизайнером. Для этого достаточно просто быть грамотным человеком. Если на страницах этой wiki вы встретите неточности, орфографические, либо пунктуационные ошибки, или просто заметите дефиc там, где должно стоять тире — исправьте, пожалуйста, текст так, как вы считаете нужным. Это можно сделать с помощью вкладки "Править" вверху каждой страницы, либо с помощью соответствующей ссылки в каждом заголовке.<br />
<br />
Если вы сомневаетесь относительно верности вашего исправления, тогда вы можете написать об этом на странице обсуждения. Не забудьте только указать место в тексте, где, по вашему мнению, находится ошибка и ваш вариант написания. Ссылка на страницу обсуждения находится так же, вверху каждой страницы.</div>Raw mathttp://man.deeptown.org/index.php/%D0%A2%D1%83%D1%82%D0%BE%D1%80%D0%B8%D0%B0%D0%BB%D1%8B_%D0%B8_HOWTOТуториалы и HOWTO2010-05-05T19:05:41Z<p>Raw mat: Правки 200.252.15.7 (обсуждение) откачены к версии Raw mat</p>
<hr />
<div>На этой странице размещаются документы, затрагивающие практические вопросы использования платформы диптауна, а так же пошаговые руководства, позволяющие научиться выполнять разнообразные операции, вроде импортирования новых моделей, написания скриптов и т. д.<br />
<br />
Если у вас есть идея или вы хотите поделиться своим опытом -- пожалуйста пишите свои HOWTO и помогите новичкам. Так же, не стоит забывать про [http://forum.deeptown.org форум], на котором можно вести обсуждение статей и задавать вопросы.<br />
<br />
При написании собственной статьи, указывайте пожалуйста свое авторство. Это можно сделать автоматически, с помощью последовательности символов '''<nowiki>--~~~~</nowiki>'''<br />
<br />
<br />
== Туториалы ==<br />
<br />
Здесь должны размещаться статьи, которые подразумевают изучение некоторой темы на некотором конкретном примере. Читатель, повторяя операции должен в итоге прийти к тем же результатам что и автор.<br />
<br />
* [[С чего начать|Первый запуск и советы новичкам]] --[[Участник:Korvin|Korvin]] 12:34, 27 апреля 2008 (EDT)<br />
* [[Тюнинг машины для достижения лучших результатов]] --[[Участник:Korvin|Korvin]] 12:34, 29 апреля 2008 (EDT)<br />
* [[Работа с флеш объектами на примере скрипта bonnet.kpp]] --[[Участник:Korvin|Korvin]] 12:34, 29 апреля 2008 (EDT)<br />
<br />
== HOWTO ==<br />
<br />
HOWTO ориентируются на более общий случай и описывают действия в более общем смысле, однако все равно должны отражать четкую последовательность действий, которая должна выполняться пользователем для достижения результата.<br />
<br />
* [[Импортирование моделей в Диптаун]] --[[Участник:Korvin|Korvin]] 12:34, 29 апреля 2008 (EDT)<br />
* [[Добавление текстуры для земли и ящиков]] --[[Участник:Stmf|Stmf]] 22:42, 21 июля 2008 (EDT)</div>Raw mathttp://man.deeptown.org/index.php/%D0%A1_%D1%87%D0%B5%D0%B3%D0%BE_%D0%BD%D0%B0%D1%87%D0%B0%D1%82%D1%8CС чего начать2010-05-05T18:53:20Z<p>Raw mat: </p>
<hr />
<div>'''Внимание: Эта страница устарела. Некоторая информация может оказаться неактуальной'''<br />
<br />
== Где я? ==<br />
<br />
Если вы читаете эту страницу, то видимо уже установили себе Deeptown SDK и хотите что либо с ним сделать :)<br />
Если еще не установили, то вам сюда: [[Deeptown SDK]].<br />
<br />
Данная страница предназначена для того, чтобы дать самое общее представление о том, что же из себя представляет система Диптауна на данный момент и что с ней можно сделать интересного. Здесь мы постараемся (в свободной форме) описать возможные пути исследования и продемонстрировать некоторые вещи на примерах.<br />
<br />
То что здесь будет описано, по большей части преследует одну цель — дать человеку отправную точку и помочь сориентироваться в многообразии информации. Мы не в состоянии объяснить все, а можем лишь указать путь :) Поэтому — пробуйте, пытайтесь, изучайте систему и делитесь своим опытом на форуме :) Кроме того, эта wiki — так же к вашим услугам. Если вам есть что добавить или исправить — милости просим.<br />
<br />
== Что это? Как попасть в Диптаун? ==<br />
<br />
SDK это набор программных средств, включающий в себя саму платформу диптауна (aka "движок"), а так же набор разнообразных, сопутствующих программ, таких как компилятор языка К++. Короче говоря, это основа, на базе которой и будет строиться Диптаун. На данный момент, "того" Диптауна еще не существует. Точнее, существуют некоторые его части у наших архитекторов, но они еще не в публичном доступе. Так что, "попадать" пока еще некуда.<br />
<br />
Тем не менее, обычные пользователи могут попробовать использовать "все это" для того, чтобы оценить возможности системы и просто посмотреть "красивые картинки", которые скоро будут. Ниже будет описано, как заставить систему работать и посмотреть некоторые уже существующие демки.<br />
<br />
Люди, желающие поглубже изучить саму систему, найдут здесь полезную информацию о том, как совершаются типовые действия, вроде добавления объектов и их программирования. Более подробно это будет рассмотрено в соответствующей документации.<br />
<br />
== Первый запуск ==<br />
[[Изображение:Showto 1.png|thumb]]<br />
[[Изображение:Showto 2.png|thumb]]<br />
Для начала попробуем запустить все "как есть". Для этого нам понадобится собственно Deeptown SDK и архив с медиа файлами, который уже должен быть при вас, если вы внимательно читали инструкцию по установке. <br />
<br />
Попробуйте запустить файл deep.exe (если вы в windows) или /opt/deeptown/bin/deep в *nix. Будьте терпеливы, потому что в первый запуск система производит индексацию содержимого каталога Media (того самого) и создает в нем файл ".index", который используется для поиска файлов самой системой. При последующих запусках эта операция выполняться не будет (только в [[Индексация DISS|определенных случаях]]).<br />
<br />
Если все прошло успешно, то вы должны будете увидеть окошко рендеринга и, через некоторое время, простейшую демосцену, загружаемую по умолчанию. На данный момент это сцена "bonnet", содержащая в себе машину и кучку кубиков. Выглядеть это должно примерно как на картинках справа. <br />
<br />
Машиной можно ездить и сбивать кубики. Изначально они расположены справа и сзади от вас (конечно это все можно изменить!)<br />
<br />
=== Управление ===<br />
<br />
* Ctrl+Q — переключиться в режим курсора (управление окошком)<br />
* Двойное нажатие на заголовке внутреннего окна сворачивает его в заголовок (чтобы меньше места занимать)<br />
* W, S, A, D — движение камерой '''при зажатой кнопке Alt'''<br />
* Стрелки клавиатуры — управление машиной (попробуйте наехать на кубики)<br />
* Стрелка назад — тормозить (если едем назад — наоборот, давить кнопку вперед)<br />
* Кнопка "spawn object" интерфейса — создает в воздухе еще один кубик, где то неподалеку. Кубики создаются всегда случайным образом (и это тоже можно изменить!).<br />
* Кнопка "cleanup" — удаляет все созданные вами кубики (те которые были изначально — созданы другим путем, поэтому они останутся на месте)<br />
<br />
'''Примечания''': <br />
* Машина (а так же форма интерфейса) управляется скриптом /media/storage/media/scripts/bonnet.kpp<br />
* Переназначить клавиши управления можно редактируя файл /media/storage/etc/world/input.conf<br />
* Чтобы выйти из системы, надо либо нажать крестик на окне интерфейса, либо нажать Alt+Tab и закрыть окно рендеринга<br />
* Если вы обнаружили что то странное, либо программа попросту "совершила недопустимую операцию", то смело идите в багтрекер по адресу [http://bugs.deeptown.org http://bugs.deeptown.org] и оставляйте свое сообщение. <br />
* Есть еще и другие сцены, которые можно попробовать запустить. Для этого надо открыть файл /media/storage/etc/boot/services и раскомментировать одну из строчек client_wm:..., не забыв закомментировать строчку, где упоминается bonnet.scene!<br />
* Текущая версия модели ведет себя не очень хорошо, из за плохой настройки физических параметров. Для улучшения характеристик модели предусмотрен [[Тюнинг_машины_для_достижения_лучших_результатов|специальный туториал]]. Выполнив его вы научитесь править модели и получите хорошую модель машинки :)<br />
<br />
== Консоль ==<br />
<br />
Консоль Диптауна является стандартным средством взаимодействия с системой, которая позволяет (точнее будет позволять) всецело управлять системой и отдавать ей команды. На данный момент она реализована только в самых общих чертах (даже стандартных команд оболочки еще нет). В будущем она будет существенно расширена и дополнена. <br />
<br />
Подключение к консоли осуществляется по протоколу Telnet, любым из клиентов, поддерживающих этот протокол.<br />
<br />
Перед первым подключением необходимо создать пользователя Диптауна (не путать с пользователем операционной системы!). Для его создания необходимо запустить сценарий mkuser.pl, который находится в папке с медиаданными. У Вас будут запрошены имя пользователя, пароль, а также идентификаторы самого пользователя и его группы.<br />
<br />
Чтобы подключиться к консоли из ОС Windows надо:<br />
# зайти в меню Пуск<br />
# открыть диалог "выполнить" (или сразу нажать Win+R)<br />
# ввести туда <tt>telnet localhost 4830</tt> и нажать Enter<br />
<br />
Линуксоиды скорее всего и сами разберутся ;)<br />
<br />
Далее, можно попробовать запустить программы, имеющиеся по умолчанию. Например можно ввести команду /bin/hello.gbc и получить простенькое тестовое окно с известным сообщением. Если Вы сами написали консольную программу и откомпилировали её с помощью kpp, не забудьте добавить полученный gbc-файл в файловую систему, как описано [[Запуск_файла_kpp_из_консоли_Диптауна|здесь]]. Просьба не судить строго, ведь это не конечный продукт, а всего лишь тесты :)<br />
<br />
Если у Вас возникнет желание написать более толковое приложение — обращайтесь к нам и [http://forum.deeptown.org на форум].<br />
<br />
Выход из консоли осуществляется вводом команды exit.<br />
<br />
== Что еще можно посмотреть? ==<br />
<br />
Практически все "интересные" вещи сосредоточены в уже известной вам директории Media. Приведем здесь список основных поддиректорий с комментариями к ним.<br />
<br />
;Media/storage: Это корневая директория для внутренней ФС диптауна. Все внутренние пути указываются относительно нее. Скажем, если в файле материала указана строка texture <tt>/media/textures/grass.png</tt>, то это значит что реальный файл расположен по адресу <tt>Media/storage/media/textures/grass.png</tt>.<br />
;Media/storage/bin: Здесь будут располагаться исполняемые файлы скриптов.<br />
;Media/storage/boot: Директория системного загрузчика (ничего интересного).<br />
;Media/storage/etc: А здесь хранятся файлы настроек. Заглянуть рекомендуется настоятельно. По крайней мере поизучать. Попробуйте загрузить другую сцену вместо той что грузится по умолчанию. '''Примечание''': Все текстовые файлы в диптауне используют формат UNIX, когда строка переводится одним символом LF. Поэтому, в windows открывать их надо либо wordpad-ом (он правильно понимает переносы), либо другим редактором, поддерживающим такие файлы. Хорошо работает редактор в FAR Manager-е.<br />
;Media/storage/home: пока пустует. <br />
;Media/storage/media: Здесь тоже много чего интересного. Это основное хранилище различных медиа данных, имеющих отношение к собственно виртуальному пространству. Содержит такие поддиректории как:<br />
::* '''materials''' — файлы материалов OGRE. Определяют графические свойства поверхностей моделей, такие как текстуры, цвета, привязанные шейдеры и др.<br />
::* '''mein''' — медиафайлы для встроенного графического интерфейса, такие как скины и шрифты<br />
::* '''meshes''' — здесь хранятся меш файлы загружаемых моделей. Более подробно про это можно почитать в статье [[импорт моделей в диптаун]].<br />
::* '''models''' — а здесь хранятся сами модели. Модель это нечто большее, чем просто меш с натянутой текстурой. Файл модели описывает также понятия, относящиеся к другим представлениям объекта, а не только к графическому. Например, там указывается масса модели, момент ее инерции и много других параметров. Смотри так же [[понятие объекта в пространстве]].<br />
::* '''scenes''' — если кратко, то сцена — это совокупность объектов, расположенных в некоторой области виртуального пространства. В этой директории хранятся файлы локальных сцен.<br />
::* '''scripts''' — самое интересное :) Здесь расположены скрипты, задающие поведение отдельных объектов. Поскольку диптаун изначально создавался как интерактивная среда, то каждый объект, находящийся в виртуальном пространстве, может быть "живым" и "разумным". С объектом ассоциируется некоторый скрипт, который программирует его действия и управляет его поведением. Например, скрипт bonnet.kpp реализует интерфейс к той самой машине и позволяет пользователю (то есть нам с вами) управлять ей. Скрипт park.kpp создает интерфейс управления анимацией для сцены park, и сам создает эту анимацию. Все скрипты написаны на нашем языке [[K++]].<br />
::* '''shaders''' — хранилище для шейдеров. Думаю что это такое, объяснять не требуется.<br />
::* '''textures''' — ну и здесь наверное все понятно.<br />
<br />
== Что еще можно сделать? ==<br />
<br />
Это зависит, в первую очередь, от вашего желания и способностей, а так же от имеющихся навыков. <br />
<br />
=== Программистам ===<br />
<br />
Если вы знакомы с такими языками программирования как Паскаль, Си++ или Java, то вам не составит труда изучить, или по крайней мере разобраться, в языке [[K++]]. Он очень похож на эти языки.<br />
<br />
<s>Попробуйте например, модифицировать скрипт bonnet.kpp так, чтобы создаваемые кубики располагались не случайным образом а один над другим. Главное поставить высоту побольше, чтобы кубики не пытались телефрагить друг друга.</s><br />
<br />
=== Моделерам ===<br />
<br />
Если вы умеете моделировать объекты в 3D пакетах, таких как 3D Studio, Maya или Blender, и желаете создать свои модели, то вам стоит обратиться в раздел [[Разработка моделей]]. Там в скором времени появится информация о том, каких правил следует придерживаться при создании моделей, а так же детально будет расписан процесс конвертирования моделей в формат Диптауна.<br />
<br />
В настоящее время, мы испытываем большой дефицит людей этой профессии, так что если вас заинтересовал проект — смело обращайтесь [http://forum.deeptown.org на форум]. Мы постараемся оказать вам всестороннуюю помощь и поддержку.<br />
<br />
=== Всем ===<br />
<br />
Как вы понимаете, написание пространной и качественной документации — длительный и трудоемкий процесс, отнимающий наше время и силы. Несмотря на то, что мы стараемся поддерживать документацию на уровне, это не всегда возможно. Поэтому, если вы желаете поделиться собственным опытом в изучении Диптауна или написать HOWTO на определенную тему — пробуйте. Эта wiki к вашим услугам :) Собственно, она и создавалась для этих целей. В ближайшее время, мы постараемся написать серию HOWTO, затрагивающих основные вопросы, вроде скриптования объектов и создания собственных сцен. Ссылки на них будут размещаться [[Туториалы и HOWTO|здесь]]. Присоединяйтесь :)<br />
<br />
Вы можете помочь проекту, даже не будучи программистом или дизайнером. Для этого достаточно просто быть грамотным человеком. Если на страницах этой wiki вы встретите неточности, орфографические, либо пунктуационные ошибки, или просто заметите дефиc там, где должно стоять тире — исправьте, пожалуйста, текст так, как вы считаете нужным. Это можно сделать с помощью вкладки "Править" вверху каждой страницы, либо с помощью соответствующей ссылки в каждом заголовке.<br />
<br />
Если вы сомневаетесь относительно верности вашего исправления, тогда вы можете написать об этом на странице обсуждения. Не забудьте только указать место в тексте, где, по вашему мнению, находится ошибка и ваш вариант написания. Ссылка на страницу обсуждения находится так же, вверху каждой страницы.</div>Raw mathttp://man.deeptown.org/index.php/Release_NotesRelease Notes2010-05-05T18:53:04Z<p>Raw mat: </p>
<hr />
<div><H2>''' Старьё! ''' </H2><br />
<br />
<br />
Данная страница отражает текущее состояние Deeptown SDK и его компонентов. Содержание этой страницы формируется из важных тонкостей, которые разработчики считают нужным донести до пользователей, а также из ответов на часто задаваемые вопросы по текущему релизу.<br />
<br />
Прежде чем написать [[Bugzilla|отчет об ошибке]], проверьте - нет ли этой ошибки в данном списке.<br />
<br />
== Ядро ==<br />
<br />
* остается неисправленной ошибка с корректным завершением работы. Проблема кроется в сетевом движке.<br />
<br />
== Компилятор [[K++]] ==<br />
<br />
* исходный код стандартной библиотеки K++: [http://dao.deeptown.org/misc/kpp.kpp kpp.kpp]<br />
* не обрабатываются спецификаторы доступа. Т.е. все, что может быть экспортировано из класса или из модуля - экспортируется из него, а ключевые слова private, protected и public игнорируются.<br />
* для объектов, работающих с консолью (STDIN, STDOUT, STDERR) в стандартной библиотеке не реализованы оберточные более удобные классы. Это связано с тем, что эти переменные устанавливаются только для текущего модуля, и из стандартной библиотеки недоступны. Ошибка будет исправлена путем изменения механизма передачи этих объектов пользовательскому коду.<br />
* не доработан интерфейс класса string в плане удобства его использования.<br />
<br />
== Подсветка синтаксиса ==<br />
<br />
Пользователи KDE, для удобства работы с исходными текстами программ, могут установить написанные нами схемы подсветки синтаксиса для Katepart. После установки схем подсветки, все редакторы, которые используют этот виджет (Kate, KWrite, KDevelop, Krusader и др.), автоматически смогут правильно подсвечивать синтаксис исходных текстов, написанных на K++ и на Gide, а так же расставлять маркеры для свертки кода (code folding). <br />
<br />
Чтобы установить схемы подсветки, необходимо скачать их XML описания:<br />
* [http://dao.deeptown.org/misc/kpp.xml Для K++]<br />
* [http://dao.deeptown.org/misc/gide.xml Для Gide]<br />
<br />
А затем скопировать их в одну из нижеприведенных директорий:<br />
~/.kde/share/apps/katepart/syntax/<br />
<путь к системным файлам kde>/<версия>/share/apps/katepart/syntax/<br />
<br />
Узнать место установки kde в вашей системе, как правило можно с помощью переменной среды KDEDIRS. Если схема будет установлена в системную директорию, то она будет доступна всем пользователям, а не только текущему.</div>Raw mat