<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="http://man.deeptown.org/skins/common/feed.css?303"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://man.deeptown.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kamillen</id>
		<title>Deeptown Manual - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://man.deeptown.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kamillen"/>
		<link rel="alternate" type="text/html" href="http://man.deeptown.org/index.php/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/Kamillen"/>
		<updated>2026-05-31T09:41:11Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.20.2</generator>

	<entry>
		<id>http://man.deeptown.org/index.php/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8</id>
		<title>Функции</title>
		<link rel="alternate" type="text/html" href="http://man.deeptown.org/index.php/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8"/>
				<updated>2010-02-19T15:50:08Z</updated>
		
		<summary type="html">&lt;p&gt;Kamillen: /* Экспортирование функций */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ни один современный язык программирования не был бы возможен без функций. Функции это &amp;quot;кирпичики&amp;quot;, из которых складывается программа. Для работы функциям, подобно обычным программам могут потребоваться входные данные — их называют аргументами, или параметрами функции. Функции так же могут возвращать результат. С этой точки зрения, функцию можно сравнить с &amp;quot;черным ящиком&amp;quot;, имеющим несколько входов и один выход. Однако, в отличие классического &amp;quot;черного ящика&amp;quot;, функция так же может изменять сами входные значения. Но в целом, смысл функций тот же, что и в математике. &lt;br /&gt;
&lt;br /&gt;
С точки зрения языка К++, каждая функция представляет собой подпрограмму, то есть некоторый участок кода, который функионирует автономно. Сами функции а так же области и примеры их применения были неоднократно рассмотрены в предыдущих главах книги. В этой главе внимание будет уделено синтаксису функций, способам их объявления и некоторым моментам, связанным с их применением.&lt;br /&gt;
&lt;br /&gt;
== Объявление ==&lt;br /&gt;
&lt;br /&gt;
Объявление любой функции начинается с указания ключевого слова &amp;lt;tt&amp;gt;'''function'''&amp;lt;/tt&amp;gt; следом за которым указывается тип возвращаемого значения, после которого идет [[идентификатор]] имени функции. Указание типа можно опустить, тогда будет подразумеваться, что функция возвращает [[Переменные#Нетипированные (динамические) переменные|переменную динамического типа]]. После указания имени функции идет блок описания ''параметров'', или ''аргументов'' функции. Блок заключается в круглые скобки; сами параметры, в ходе описания отделяются друг от друга запятой. Завершает конструкцию ''тело функции'', которое записывается в фигурных скобках. &lt;br /&gt;
&lt;br /&gt;
Вот примеры объявления функций:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function MyFunction() { return 5; }&lt;br /&gt;
function Compare(x, y) { return x &amp;lt; y; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Первая функция не принимает параметров, но возвращает числовую константу 5. Согласитесь, не очень полезный код. Вторая функция немного &amp;quot;поумнее&amp;quot;: она принимает два объекта и пытается их сравнить, используя оператор отношения &amp;quot;меньше&amp;quot;. Результатом выполнения такой функции будет логическое значение &amp;quot;истина&amp;quot;, если ''x'' и вправду меньше чем ''y'', либо ложь в противном случае. Для правильной работы этой функции требуется, чтобы передаваемые параметры допускали возможность сравнения (то есть, были бы определены соответствующие [[Выражения#Операторы|операторы]]). Если этого нет, — будет сгенерировано [[Идеология языка#Понятие исключения|исключение]], то есть ошибка времени исполнения. Поскольку типы параметров никак не указаны, компилятор не имеет возможности контролировать фактические типы передаваемых параметров, а следовательно не может предупредить программиста если по его мнению что-то не так. Тем не менее, это обеспечивает программиста возможностью написания гибких программ и функций, которые не зависят от конкретных типов передаваемых данных.&lt;br /&gt;
&lt;br /&gt;
Несмотря на простоту последней функции, подобный код может с успехом применяться в реальных программах. Пример такого кода будет приведен при описании [[Блоки|блоков]], чуть дальше по ходу книги.&lt;br /&gt;
&lt;br /&gt;
== Аргументы ==&lt;br /&gt;
&lt;br /&gt;
Аргументы функции — это та информация, которую программист хочет передать в функцию ее для последующей обработки. Как уже было показано ранее, в качестве аргументов функций могут передаваться любые объекты, любых типов. При этом, будет генерироваться динамический код, не привязанный к конкретным типам данных. Такие функции могут применяться в случаях, когда они должны принимать в качестве параметров целый набор объектов различных типов. Чтобы повысить надежность кода (а также дать компилятору информацию для оптимизации), следует применять типизацию аргументов (рекомендуется).&lt;br /&gt;
&lt;br /&gt;
=== Типизация аргументов ===&lt;br /&gt;
&lt;br /&gt;
Если функция подразумевает передачу параметров строго определенного типа, то применяется расширенная форма записи аргументов. При этом, идентификатор имени параметра предворяется именем типа, который следует принимать. Например, вышеописанную функцию &amp;lt;tt&amp;gt;Compare()&amp;lt;/tt&amp;gt; мы можем переписать так, чтобы она принимала в качестве параметров только объекты, представляющие собой целые числа. При этом, имена переменных ''x'' и ''y'' мы дополняем сведениями о типе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function Compare(int x, int y) { return x &amp;lt; y; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь, в целях уменьшения вероятности ошибок, при компиляции вызовов такой функции, комилятор будет проверять соответствие типов фактически переданных параметров и типов, указанных в объявлении функции. Если типы различны, то компилятор попытается выполнить операцию ''приведения типов'', если же типы неприводимы — будет сгенерирована ошибка времени компиляции.&lt;br /&gt;
&lt;br /&gt;
В общем случае, в описании функции можно указывать параметры любых типов, и даже смешивать типированные и нетипированные параметры:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function MyFunction(int p1, string p2, block p3) { /* тело функции */ }&lt;br /&gt;
function OtherFunction(MyClass p1, p2) { /* тело функции */ }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как видно из кода, функция &amp;lt;tt&amp;gt;MyFunction()&amp;lt;/tt&amp;gt; имеет три параметра: [[Стандартные типы данных#Целые числа|целочисленный]] ''p1'', [[Стандартные типы данных#Строки|строковый]] ''p2'' и [[Блоки|блок]] ''p3''. &lt;br /&gt;
&lt;br /&gt;
Функция &amp;lt;tt&amp;gt;OtherFunction()&amp;lt;/tt&amp;gt;, в качестве параметров может принимать экземпляры класса &amp;lt;tt&amp;gt;MyClass&amp;lt;/tt&amp;gt; (параметр ''p1''), и объекты любого типа в качестве параметра ''p2''. '''Обратите внимание''', что может показаться, что тип &amp;lt;tt&amp;gt;MyClass &amp;lt;/tt&amp;gt; указан для обоих аргументов, но на самом деле это не так. Тип привязывается только к переменной, указанной сразу после него. Более подробно, эта проблема рассмотрена в главе [[Объявление переменных и констант]].&lt;br /&gt;
&lt;br /&gt;
Приведем несколько примеров вызова функции с различными наборами параметров:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
var myblock = { |x| x += 2; };&lt;br /&gt;
MyFunction(10, &amp;quot;hello&amp;quot;, myblock); //верно, типы фактических параметров совпадают&lt;br /&gt;
MyFunction(&amp;quot;20&amp;quot;, 10, myblock);    //частично верно, выполняется операция приведения&lt;br /&gt;
MyFunction([1,2,3], myblock, 5);  //неверно. переданы неприводимые типы&lt;br /&gt;
&lt;br /&gt;
var o1 = new MyClass;&lt;br /&gt;
var o2 = new OtherClass;&lt;br /&gt;
OtherFunction(o1, o2);&lt;br /&gt;
OtherFunction(o2, o1);&lt;br /&gt;
OtherFunction(o1, o1);&lt;br /&gt;
OtherFunction(o2, o2);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;1: Мы создаем экземпляр переменной-[[Блоки|блока]], который будет передаватсья в качестве параметра функциям. Здесь он не имеет особого значения, так что на него можно практически не обращать внимания (важен только его тип).&lt;br /&gt;
&lt;br /&gt;
;2-4: Производятся вызовы функции &amp;lt;tt&amp;gt;MyFunction()&amp;lt;/tt&amp;gt; с различными наборами параметров. В первом вызове типы фактических параметров точно совпадают с типами в описании функции, следовательно никакого приведения не происходит и все работает как есть. Во втором вызове происходит приведение строковой константы &amp;quot;20&amp;quot; к числу, а числа 10 к строке. &amp;quot;Частично&amp;quot; верна эта конструкция потому, что на момент компиляции невозможно определить, сработает ли первая операция приведения (строки к числу) или нет. Если строка содержит нецифровые символы, то в результате операции приведения будет сгенерировано [[Идеология языка#Понятие исключения|исключение]]. Разумеется в данном случае, все будет хорошо. Второе приведение, а именно числа 20 к строке так же выполнится успешно, потому что абсолютно любое число можно представить в виде строки символов, соответствующих цифрам числа.&lt;br /&gt;
&lt;br /&gt;
: Третий вызов функции &amp;lt;tt&amp;gt;MyFunction()&amp;lt;/tt&amp;gt; является совершенно неверным, поскольку все фактические параметры переданные в функцию, являются неприводимыми к соответствующим типам формальных параметров.&lt;br /&gt;
&lt;br /&gt;
;6-11: Здесь создаются две инстанции классов &amp;lt;tt&amp;gt;MyClass&amp;lt;/tt&amp;gt; и некоторого класса &amp;lt;tt&amp;gt;OtherClass&amp;lt;/tt&amp;gt;. Далее выполняется вызов функции &amp;lt;tt&amp;gt;OtherFunction()&amp;lt;/tt&amp;gt;, которой в разных комбинациях передаются вышесозданные объекты. В качестве упражнения, Читателю предлагается самому решить, какие из вызовов верные а какие нет. &lt;br /&gt;
&lt;br /&gt;
: Постарайтесь ответить на следующие вопросы:&lt;br /&gt;
:* Какие из вызовов приведут к ошибке времени компиляции?&lt;br /&gt;
:* Какие из вызовов приведут к [[Идеология языка#Понятие исключения|исключению]] (ошибке времени исполнения)?&lt;br /&gt;
:* Что изменится, если реализовать оператор приведения типа &amp;lt;tt&amp;gt;MyClass&amp;lt;/tt&amp;gt; к &amp;lt;tt&amp;gt;OtherClass&amp;lt;/tt&amp;gt;?&lt;br /&gt;
:* Что изменится, если реализовать оператор приведения типа &amp;lt;tt&amp;gt;OtherClass&amp;lt;/tt&amp;gt; к &amp;lt;tt&amp;gt;MyClass&amp;lt;/tt&amp;gt;?&lt;br /&gt;
:* Что изменится, если типы будут взаимноприводимыми?&lt;br /&gt;
&lt;br /&gt;
=== Инициализаторы аргументов (значения по умолчанию) ===&lt;br /&gt;
&lt;br /&gt;
В некоторых случаях может возникнуть необходимость задания параметров функции по умолчанию, либо возможность указания только части параметров. Например, функция может иметь обширный список аргументов, большая часть которых обычно не используется. То есть, они влияют на некоторые очень специфичные свойства объекта, которые редко применяются. Если такую функцию описывать традиционным образом, то получится что программист, использующий ее в своей программе, должен будет каждый раз писать что-то вроде:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
var x = SomeWierdFunction(1, &amp;quot;a&amp;quot;, 0, 0, 0, 0, 0, 0);&lt;br /&gt;
...&lt;br /&gt;
var y = SomeWierdFunction(2, &amp;quot;b&amp;quot;, 0, 0, 0, 0, 0, 0);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Такие вызовы портят внешний вид кода и усложняют его написание (особенно если параметры по умолчанию далеко не такие простые, как в этом примере). Желательно было бы сделать так, чтобы функция сама &amp;quot;знала&amp;quot;, какие значения параметров нужно указать, если в коде вызова они были опущены. Для этой цели применяются инициализаторы. Инициализаторы аргументов (их еще называют &amp;quot;значениями по умолчанию&amp;quot;), подобно инициализаторам обычных переменных устанавливают начальное значение аргумента, но только если оно не было установлено явным образом при вызове данной функции. &lt;br /&gt;
&lt;br /&gt;
Приведем более приближенный к реальности пример. Допустим, программист решил написать свою реализацию некоторого класса контейнера, служащего для содержания других объектов. Предположим так же, что данный класс подобно [[Стандартные типы данных#Массивы и списки|массиву]] позволяет обратиться к своим элементам через индекс и имеет некоторое свойство, показывающее текущий размер коллекции. Программист желает написать функцию, возвращающую некоторое подмножество данной коллекции, расположенное между верхним и нижним индексами. Логично предположить, что может возникнуть желание указать только один из индексов, в то время как другой должен подставиться как граница коллекции с соответствующей стороны. То есть, в случае нижнего индекса, граничное значение будет равно нулю, в то время как граничное значение для верхнего индекса заранее не определено. Фактически, оно равно количеству размещенных в коллекции элементов минус один (если нумерация ведется с нуля). Разумеется, это число меняется по мере добавления и удаления элементов. Если бы инициализаторов не существовало, программисту приходилось бы каждый раз явно указывать верхнюю границу выборки на основании его представления о текущем количестве элементов в коллекции (еще одно потенциальное место для ошибки). Это по меньшей мере неудобно. По большей — невозможно, ведь не всегда заранее известно, какое количество элементов содержится в коллекции в данный момент. В общем, с учетом существования инициализаторов, интерфейс класса коллекции мог бы выглядеть примерно так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
class MyCollection {&lt;br /&gt;
    public function int Add(...); //добавление элемента&lt;br /&gt;
    private function GetLength(); // размер коллекции&lt;br /&gt;
    property length read GetLength;&lt;br /&gt;
    const function MyCollection SubCollection(int from = 0, int to = this.length - 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В первой строке объявляется метод добавления элемента в массив. Для удобства применения класса, он оформлен в виде функции с переменным списком аргументов (см. ниже). Если говорить коротко, то это функция, которая может принимать различное (сколь угодно большое) количество параметров в зависимости от вызова. При этом, они не описываются в заголовке функции, а вместо них ставится ''оператор-многоточие'' (&amp;lt;tt&amp;gt;...&amp;lt;/tt&amp;gt;). Это необходимо, чтобы отличить такую функцию от обычной функции. При вызове, фактические параметры автоматически добавляются в массив, который можно использовать внутри функции.&lt;br /&gt;
&lt;br /&gt;
Далее следуют аксессор, возвращающий текущее количество элементов в коллекции и свойство, связанное с ним.&lt;br /&gt;
&lt;br /&gt;
Последняя строка как раз представляет для нас интерес. Как видно из описания, метод &amp;lt;tt&amp;gt;SubCollection()&amp;lt;/tt&amp;gt; возвращает некоторе подмножество класса, которое само является экземпляром данного класса; границы выборки задаются двумя параметрами: нижним индексом ''from'' и верхним ''to''. Аргументы, соответственно, инициализированы нулем и выражением доступа к свойству &amp;lt;tt&amp;gt;length&amp;lt;/tt&amp;gt;, которое будет возвращать значение верхнего индекса. Поскольку в выражении есть ссылка на текущий экземпляр (&amp;lt;tt&amp;gt;'''this'''&amp;lt;/tt&amp;gt;), то значение выражения будет зависеть от того, метод какого экземпляра вызывается в каждом конкретном случае.&lt;br /&gt;
&lt;br /&gt;
Вот пример некоторого участка программы, использующей вышеописанный класс:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
var c = new MyCollection;&lt;br /&gt;
MyCollection.Add(1, &amp;quot;a&amp;quot;, [2], { |x| x + 1; }, { 1 =&amp;gt; 'a', 2 =&amp;gt; 'b'});&lt;br /&gt;
var sub1 = c.SubCollection();&lt;br /&gt;
var sub2 = c.SubCollection(3);&lt;br /&gt;
var sub3 = c.SubCollection(1, 4);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Модификаторы и копирование ===&lt;br /&gt;
&lt;br /&gt;
Довольно часто создаются функции, принимающие в качестве параметров не отдельные переменные, вроде чисел строк или блоков, а целые массивы. Например, программисту может потребоваться написать функцию, рассчитывающую математическое ожидание некоторой величины. При этом, в качестве параметров функция будет принимать массив значений и массив соответствущих значениям вероятностей; вычисленное значение математического ожидания возвращается в качестве значения функции.&lt;br /&gt;
&lt;br /&gt;
Математическое ожидание, это величина соответствующая наиболее вероятному из значений исходной величины. Для набора дискретных величин (а именно такой случай мы и рассматриваем) она рассчитывается, как сумма произведений значения величины на ее вероятность:&lt;br /&gt;
&lt;br /&gt;
:[[Изображение:DMX.png]] &amp;lt;!--:&amp;lt;math&amp;gt;MX = \sum\limits_{i=1}^n x_i \cdot p_i&amp;lt;/math&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Приведем пример функции, реализующей эту формулу:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
function real AOD(array values, array probs) {&lt;br /&gt;
    if (values.size != probs.size) //проверка исходных данных&lt;br /&gt;
        throw &amp;quot;массивы должны иметь одинаковое количество элементов&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    var real M; //счетчик и накопитель для величины математического ожидания&lt;br /&gt;
    for (var i = 0; i &amp;lt; values.size; ++i)&lt;br /&gt;
        M += values[i] * probs[i];&lt;br /&gt;
    return M;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;1-2: Для того чтобы значение можно было рассчитать, размеры исходных массивов должны совпадать. Если они не совпадают, то генерируется [[Идеология языка#Понятие исключения|исключение]] с соответствующим сообщением об ошибке.&lt;br /&gt;
&lt;br /&gt;
;4: Здесь мы объявляем переменную счетчик, которая будет использоваться в цикле и переменную, накапливающую сумму, то есть конечную величину математического ожидания. &lt;br /&gt;
&lt;br /&gt;
;5-8: Для рассчета значения применяется цикл &amp;lt;tt&amp;gt;'''for'''&amp;lt;/tt&amp;gt;, который перебирает переменную-счетчик ''i'' начиная с нуля и заканчивая последним индексом массивов. Тело цикла представляет собой вышеописанную формулу. Результат возвращается с помощью оператора &amp;lt;tt&amp;gt;'''return'''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь, если мы создадим два массива и заполним их некоторыми значениями, то мы сможем вычислить для них значение математического ожидания:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
var v = [-3,   -2,   -1,  0,   1,    2,    3   ]; //значения&lt;br /&gt;
var p = [0.01, 0.05, 0.2, 0.3, 0.35, 0.05, 0.04]; //вероятности&lt;br /&gt;
var avg = AOD(v, p);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С точки зрения математики вышеприведенный пример верен. Однако, существует одна проблема, которая может существенно сказаться на производительности работы системы и на объемах используемой памяти. Дело заключается в том, что при передаче параметров происходит копирование объекта параметра с передачей в функцию уже его копии. Это сделано для того, чтобы функция могла свободно распоряжаться параметрами, как обычными локальными переменными, без опасности случайно изменить значение внешней переменной. &lt;br /&gt;
&lt;br /&gt;
Таким образом, в вышеприведенном примере при вызове функции &amp;lt;tt&amp;gt;AOD()&amp;lt;/tt&amp;gt;, будет произведено копирование двух переданных массивов, а значит и всех их элементов. Естественно, чем больше размеры массивов, тем больший объем данных приходится копировать. На практике, статистические массивы могут иметь тысячи и даже десятки тысяч значений!&lt;br /&gt;
&lt;br /&gt;
Чтобы решить эту проблему, мы должны указать компилятору, что объекты параметров копировать не нужно. Мы, в свою очередь &amp;quot;обещаем&amp;quot; не изменять их значения, так что вызывающий код будет &amp;quot;спокоен&amp;quot;, что с переданными объектами ничего не случится. Это осуществляется с помощью ключевого слова-спецификатора &amp;lt;tt&amp;gt;'''const'''&amp;lt;/tt&amp;gt;, которое указывается в блоке описания параметров непосредственно перед самим параметром. Оно указывает, что данный параметр не будет изменяться в функции, что позволит компилятору произвести соответствующую оптимизацию. При этом, внутрь функции будет передан сам объект, точнее ссылка на него. Разумеется, компилятор так же следит за тем, чтобы в коде функции не было попыток изменить значение такого параметра, а если это все же случится, — будет сгенерирована ошибка времени компиляции. &lt;br /&gt;
&lt;br /&gt;
Перепишем вышеприведенный пример с использованием спецификаторов &amp;lt;tt&amp;gt;'''const'''&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function real AOD(const array values, const array probs) {&lt;br /&gt;
    if (values.size != probs.size) //проверка исходных данных&lt;br /&gt;
        throw &amp;quot;массивы должны иметь одинаковое количество элементов&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    var real M; //счетчик и накопитель для величины математического ожидания&lt;br /&gt;
    for (var i = 0; i &amp;lt; values.size; ++i)&lt;br /&gt;
        M += values[i] * probs[i];&lt;br /&gt;
    return M;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В вышеописанной функции, массивы используются только для вычисления значения математического ожидания и их изменения не происходит, так что применение спецификатора никак не скажется на реализации самой функции (тело функции фактически осталось неизмененным), но теперь код сможет работать намного быстрее (при большом количестве элементов). Это как раз тот случай, когда внешне незначительные изменения программы существенно влияют на ее производительность. Поэтому, хороший программист всегда должен держать в уме особенности конкретного языка программирования и учитывать их при написании программ.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Но что если нам необходимо решить прямо противоположную задачу? А именно, передать в функцию некоторый объект, для его изменения внутри функции. Если просто написать код вида:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function mutate(int x) { &lt;br /&gt;
    puts(&amp;quot;x = #{x}&amp;quot;); // вывод: x = 5&lt;br /&gt;
    x = 10; &lt;br /&gt;
    puts(&amp;quot;x = #{x}&amp;quot;); // вывод: x = 10&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
var v = 5;&lt;br /&gt;
mutate(v);&lt;br /&gt;
puts(&amp;quot;v = #{v}&amp;quot;);     // вывод: v = 5&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
...то ничего не получится. Да, переменная ''v'' будет передана внутрь функции &amp;lt;tt&amp;gt;mutate()&amp;lt;/tt&amp;gt;. Но при передаче параметра произойдет его копирование. Таким образом, внутри функции будет изменено значение копии, которая будет уничтожена при выходе из функции. Изначальная переменная как была установлена, так и осталась равной пяти.&lt;br /&gt;
&lt;br /&gt;
Чтобы указать компилятору, что в функцию следует передавать сам объект, а не его копию — следует указать ключевое слово-спецификатор &amp;lt;tt&amp;gt;'''var'''&amp;lt;/tt&amp;gt; (по аналогии с &amp;lt;tt&amp;gt;'''const'''&amp;lt;/tt&amp;gt;). При этом, копироваться аргумент не будет но будет возможно его изменение в теле функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function mutate(var int x) { &lt;br /&gt;
    puts(&amp;quot;x = #{x}&amp;quot;); // вывод: x = 5&lt;br /&gt;
    x = 10; &lt;br /&gt;
    puts(&amp;quot;x = #{x}&amp;quot;); // вывод: x = 10&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
var v = 5;&lt;br /&gt;
mutate(v);&lt;br /&gt;
puts(&amp;quot;v = #{v}&amp;quot;);     // вывод: v = 10&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Функции с переменным списком аргументов ===&lt;br /&gt;
&lt;br /&gt;
Бывают случаи, когда заранее не известно, какое количество параметров должна принимать функция. В качестве примера можно привести задачу форматируемого вывода. Форматируемый вывод является удобным средством формирования строки, содержащей как текст так и значения некоторых переменных, связанных с этим текстом. Предположим, что на основании статистических данных, нам надо вывести строку следующего содержания:&lt;br /&gt;
&lt;br /&gt;
 Пользователь vpupkin (501) обращался к файлу '~/docs/index.txt' 122 раз(а).&lt;br /&gt;
&lt;br /&gt;
Данная строка содержит как чисто текстовую информацию, так и значения, которые мы должны &amp;quot;внедрить&amp;quot; в текст. &amp;quot;Лобовое&amp;quot; решение проблемы может выглядеть примерно так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
var myfile = '~/docs/index.txt'; //имя файла&lt;br /&gt;
var fstat = File.stat(myfile); //получение информации о файле&lt;br /&gt;
var uname = 'vpupkin'; //имя пользователя&lt;br /&gt;
var userid = 501; //системный идентификатор пользователя&lt;br /&gt;
print(&amp;quot;Пользователь &amp;quot; + uname + &amp;quot;(&amp;quot; + userid + &lt;br /&gt;
    &amp;quot;)  обращался к файлу '&amp;quot; + myfile + &amp;quot;' &amp;quot; + &lt;br /&gt;
    fstat.times_accessed(userid) + &amp;quot; раз(а).\n&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Согласитесь, функция вывода выглядит не очень привлекательно. Она изобилует операторами конкатенации строк и прочей информацией, которая разбросана по всему вызову. Попробуем ее переписать с помощью ''оператора-вставки'' (см. главу [[Стандартные типы данных#Строки|Стандартные типы данных]]): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
print(&amp;quot;Пользователь #{uname} (#{userid}) обращался к файлу #{myfile} &amp;quot; + &lt;br /&gt;
    &amp;quot;#{fstat.times_accessed(userid)} раз(а).\n&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В такой реализации, код стал намного более читаемым, однако есть еще одна проблема. Что если впоследствии потребуется написать версию программы, поддерживающую другой язык интерфейса? Пришлось бы либо писать свою копию программы для каждой языковой версии, либо каким-то образом отделить строковые данные от вставок и вынести их в отдельный файл ресурсов. Тогда, для того чтобы перевести интерфейс программы на другой язык, достаточно будет всего лишь отредактировать файл ресурсов, без необходимости изменения текстов программы.&lt;br /&gt;
&lt;br /&gt;
Хорошим решением для этого является форматируемый вывод. При этом, операция вывода сводится к указанию строки-''формата'', а так же набора значений переменных, которые необходимо внедрить в текст. Для вышеприведенного примера, строки формата в зависимости от языка будут выглядеть так:&lt;br /&gt;
&lt;br /&gt;
 Пользователь % (%d) обращался к файлу '%' %d раз(а).\n&lt;br /&gt;
 User % (%d) accessed file '%' %d times.\n&lt;br /&gt;
&lt;br /&gt;
Каждый символ &amp;lt;tt&amp;gt;%&amp;lt;/tt&amp;gt; соответствует одному значению переменной из последующего списка. При разборе (форматировании) такой строки, каждый символ &amp;lt;tt&amp;gt;%&amp;lt;/tt&amp;gt; будет замещен на значение соответствующей переменной. Для вывода самого символа % применяется escape последовательность из двух подряд идущих символов &amp;lt;tt&amp;gt;%%&amp;lt;/tt&amp;gt;. Пара символов &amp;lt;tt&amp;gt;%d&amp;lt;/tt&amp;gt; в случае строки форматирования, означает необходимость выполнения приведения типа числа к строке.&lt;br /&gt;
&lt;br /&gt;
Таким образом, в функцию форматирования необходимо передать как минимуму один параметр — формат. Дальнейшие параметры и их количество будут зависеть уже от самой строки формата. Но как это описать в терминах языка программирования? Одним из решений может быть передача в функцию [[Стандартные типы данных#Массивы и списки|массива]] с параметрами. Тогда, код описания функции форматируемого вывода и ее вызова может выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function printf(const string format, const array args);&lt;br /&gt;
//...&lt;br /&gt;
printf(&amp;quot;Пользователь % (%d) обращался к файлу '%' %d раз(а).\n&amp;quot;, &lt;br /&gt;
    [uname, userid, myfile, fstat.times_accessed(userid)]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для вывода результирующей строки, ищутся вхождения символов &amp;lt;tt&amp;gt;%&amp;lt;/tt&amp;gt; (с возможными спецификаторами, как в случае с &amp;lt;tt&amp;gt;%d&amp;lt;/tt&amp;gt;) в строке формата и подставлять значение текущего элемента массива. После подстановки, текущим элементом становится следующий элемент массива. &lt;br /&gt;
&lt;br /&gt;
Более красивое решение заключается в использовании ''оператора-многоточие'' (&amp;lt;tt&amp;gt;...&amp;lt;/tt&amp;gt;) в объявлении функции, который указывет, что функция должна принимать переменное количество аргументов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function printf(const string format, ...);&lt;br /&gt;
//...&lt;br /&gt;
printf(&amp;quot;Пользователь % (%d) обращался к файлу '%' %d раз(а).\n&amp;quot;, &lt;br /&gt;
    uname, userid, myfile, fstat.times_accessed(userid));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь, код вызова такой функции ничем не отличается от вызова любой другой функции или метода. Программисту не нужно помнить об особенностях функции — нужно просто записывать параметры один за одним. Обладая всей необходимой информацией, компилятор языка сам сформирует массив передаваемых параметров, который будет передан в функцию. Для разбора аргументов внутри функции используется специальный метод &amp;lt;tt&amp;gt;args()&amp;lt;/tt&amp;gt;, возвращающий массив аргументов.&lt;br /&gt;
&lt;br /&gt;
'''Примечание 1:''' на самом деле, приведенная в примерах функция &amp;lt;tt&amp;gt;printf()&amp;lt;/tt&amp;gt;, реализована как расширение класса &amp;lt;tt&amp;gt;[[Стандартные типы данных#Строки|string]]&amp;lt;/tt&amp;gt; стандартной билбиотеки языка К++. Желающие реализовать форматируемый вывод могут ее использовать. Здесь же она была приведена, как наиболее подходящий пример функции с переменным списком аргументов.&lt;br /&gt;
&lt;br /&gt;
'''Примечание 2:''' В некоторых случаях заранее неизвестно, какие именно наборы параметров будут востребованы больше других, с тем чтобы переместить их ближе к началу описания. Либо метод принимает большое количество параметров, которые могут использоваться &amp;quot;стихийно&amp;quot;. Примерами таких методов могут послужить методы открытия соединения. Такие методы могут принимать большое количество параметров, как то:&lt;br /&gt;
* адрес назначения&lt;br /&gt;
* порт назначения&lt;br /&gt;
* данные авторизации (логин, пароль)&lt;br /&gt;
* настройки сжатия&lt;br /&gt;
* настройки шифрования&lt;br /&gt;
* таймаут соедиенения&lt;br /&gt;
* прочая информация&lt;br /&gt;
&lt;br /&gt;
Реально из всей этой кучи, могут использоваться 2-3 параметра, в то время как остальные остаются по умолчанию. Проблема заключается в том, что не всегда возможно описать параметры так чтобы необходимые значения возможно было передавать по порядку. Если же требуется задействовать параметр из конца списка, придется указывать и промежуточные параметры, устанавливая их значения в &amp;lt;tt&amp;gt;'''null'''&amp;lt;/tt&amp;gt;. Это засоряет код и может привести к ошибкам.&lt;br /&gt;
&lt;br /&gt;
Решением этой ситуации может послужить использование [[Стандартные типы данных#Хеши|хешей]] для передачи только тех параметров, которые необходимы. Тогда наша функция объявляется таким образом:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function connect(const hash params);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В теле функции мы работаем с хешем ''params'' доставая из него значения по мере необходимости. При отсутствии некоторой записи в хеше, считается что необходимо использовать значение по умолчанию.&lt;br /&gt;
&lt;br /&gt;
Вызов такого метода может осуществляться либо традиционным образом, когда хеш создается за пределами метода, а последнему передается сама переменная, либо с помощью специального синтаксиса:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
var params = { :address =&amp;gt; 'www.deeptown.org', &lt;br /&gt;
               :port =&amp;gt; 1234, :login =&amp;gt; 'guest' };&lt;br /&gt;
connect(params); //традиционный способ вызова&lt;br /&gt;
connect(:address =&amp;gt; 'www.deeptown.org', :timeout =&amp;gt; 20); //альтернативный&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как видно из примера, в последней строке происходит вызов метода, одновременно с объявлением хеша &amp;quot;на месте&amp;quot;. Такая форма подразумевает, что соответствующая функция должна иметь хеш в качестве последнего параметра.&lt;br /&gt;
&lt;br /&gt;
== Возврат значения ==&lt;br /&gt;
&lt;br /&gt;
Для выхода из функции применяется оператор &amp;lt;tt&amp;gt;'''return'''&amp;lt;/tt&amp;gt;, после которого указывается выражение, значение которого необходимо вернуть в качестве значения функции. Если функция ничего не возвращает, то есть тип результата определен как &amp;lt;tt&amp;gt;void&amp;lt;/tt&amp;gt;, то выражение результата не указывается. Если тип возвоащаемого значения не указан, то подразумевается что функция возвращает  [[Переменные#Нетипированные (динамические) переменные|нетипированную переменную]]; если тип указан явно, то при возврате значения, тип результата выражения будет приведен к типу результата. Если типы неприводимы, то будет сгенерирована ошибка времени компиляции.&lt;br /&gt;
&lt;br /&gt;
Приведем примеры объявления функций с различными типами возвращаемых значений и прокомментируем каждый из случаев:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function F1(int x) { &lt;br /&gt;
    if (x &amp;gt; 5)&lt;br /&gt;
        return &amp;quot;hello world&amp;quot;;&lt;br /&gt;
    else &lt;br /&gt;
        return x;&lt;br /&gt;
}&lt;br /&gt;
function string F2() { return 10; }&lt;br /&gt;
function void F3() { return; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Первая функция возвращает переменную [[Переменные#Нетипированные (динамические) переменные|динамического типа]], которая может быть либо строкой, либо числом в зависимости от значения аргумента. Вторая функция возвращает строку, но в качестве выражения в операторе &amp;lt;tt&amp;gt;'''return'''&amp;lt;/tt&amp;gt; указана константа типа &amp;lt;tt&amp;gt;[[Стандартные типы данных#Целые числа|int]]&amp;lt;/tt&amp;gt;, поэтому будет выполнена операция приведения. Третья функция не возвращает результата, поэтому оператор &amp;lt;tt&amp;gt;'''return'''&amp;lt;/tt&amp;gt; записан без выражения.&lt;br /&gt;
&lt;br /&gt;
== Локальные переменные ==&lt;br /&gt;
&lt;br /&gt;
Существуют несколько видов переменных, каждый из которых объявляется в своей области и действует в ее пределах. ''Глобальные'' переменные объявляются в контексте пакета (файла) и действуют в его пределах. Такие переменные могут быть доступны из любой функции или метода, объявленных в том же пакете. Переменные, объявленные в теле класса называются ''полями''. Их область применения ограничена методами этого класса и его потомков (более точно поведение задается с помощью спецификаторов доступа); за пределами класса такие переменные не видны.&lt;br /&gt;
&lt;br /&gt;
Самой узкой областью действия обладают локальные переменные. Они объявляются и действуют в пределах отдельной функции, или даже ее части. Локальные переменные служат для хранения промежуточных результатов рассчетов, хранения состояния локальных объектов и другой временной информации. При выходе контекста управления из области действия переменой (например при выходе из функции) она уничножается.&lt;br /&gt;
&lt;br /&gt;
=== Объявление ===&lt;br /&gt;
&lt;br /&gt;
Объявление локальных переменных ничем не отличается от объявления переменной в любой другой области. Переменные могут объявляться в любом месте функции или метода. Единственное, что нужно иметь в виду: переменная должна быть объявлена до места ее использования. Хорошей практикой является объявление переменных как можно ближе к месту их применения. Одними из существенных недостатков языка программирования Паскаль можно назвать отсутствие возможности объявления переменных прямо по ходу программы. Видимо, авторы Паскаля считали, что это дезорганизует программиста и делает его код менее читаемым. Однако, практика показывает, что при программировании достаточно больших процедур и функций, это требование только мешает. Программисту приходится &amp;quot;бегать&amp;quot; вперед-назад от текущего кода к блоку описания переменных и наоборот. При этом, вместо собственно решения проблемы, он занимается оформлением кода и разбором получившейся кучи локальных переменных, сваленных в одном месте. Это сильно отвлекает и в конечном счете ведет к появлению ошибок. Практика объявления переменных по мере использования помогает программисту сосредоточиться на текущем месте программы, не отвлекаясь от него и имея весь код &amp;quot;на виду&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
=== Область видимости ===&lt;br /&gt;
&lt;br /&gt;
Как уже было сказано выше, каждая переменная действует в пределах своей области видимости. Для локальных переменых, областью видимости является тот блок программы, в пределах которого она была объявлена. Она будет доступна в пределах всего блока, а так же во всех вложенных блоках. Если во вложенном блоке объявляется переменная с тем же именем что уже была объявлена ранее, то этому имени в коде программы будет сопостовляться внутренняя переменная. При изменении внутренней переменной значение внешней не меняется. За пределами блока переменная считается необъявленной, то есть ее использование в выражениях приведет к ошибке времени компиляции. &lt;br /&gt;
&lt;br /&gt;
Ниже приведен пример некоторой функции, в которой рассмотрены типовые ситуации, возникающие при работе с локальными переменными. В качестве упражнения, постарайтесь ответить на вопрос, какими будут значения переменных на момент вызова функции &amp;lt;tt&amp;gt;print()&amp;lt;/tt&amp;gt; в каждом из случаев. Записывайте ваши ответы по ходу разбора листинга, а затем сравните их с [[Ответы к упражнению (область видимости)|контрольными ответами]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
function f(const int p) {&lt;br /&gt;
    var x = 1, k = 2;&lt;br /&gt;
    print(&amp;quot;x = #{x}, k = #{k}\n&amp;quot;); //(1)&lt;br /&gt;
    &lt;br /&gt;
    {&lt;br /&gt;
        x = 5;&lt;br /&gt;
        var k = 3;&lt;br /&gt;
        print(&amp;quot;x = #{x}, k = #{k}\n&amp;quot;); //(2)&lt;br /&gt;
    }&lt;br /&gt;
    print(&amp;quot;x = #{x}, k = #{k}\n&amp;quot;); //(3)&lt;br /&gt;
&lt;br /&gt;
    if (p &amp;gt; 5) {&lt;br /&gt;
        print(&amp;quot;x = #{x}\n&amp;quot;); //(4)&lt;br /&gt;
    } else {&lt;br /&gt;
        var x = 10;&lt;br /&gt;
        var y = &amp;quot;hello&amp;quot;;&lt;br /&gt;
        print(&amp;quot;x = #{x}, #{y}\n&amp;quot;); //(5)&lt;br /&gt;
        x = 6;&lt;br /&gt;
    }&lt;br /&gt;
    print(&amp;quot;x = #{x}\n&amp;quot;); //(6)&lt;br /&gt;
    print(&amp;quot;#{y}&amp;quot;); //(7)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Примечание:''' В вышеприведенном коде, в строках 5-9 применен inline блок, который применяется для ''изоляции локальных переменных'' в некоторой области. Это может быть удобно в тех случаях, когда программист желает выполнить некоторые действия в пределах части функции, при том что все создаваемые временные переменные будут сконцентрированы в области этого блока. При выходе контекста выполнения программы за пределы этого блока, переменные будут освобождены при следующей операции сборки мусора (однако, повторно использовать идентификаторы можно сразу после блока).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- для того чтобы быть уверенным что используемые переменные будут своевременно освобождены. Примером такой ситуации может послужить работа с файлами. Предположим, что мы должны прочитать некоторый файл, после чего выполнить долгую и вычислительно сложную операцию по его обработке. Для обеспечения целостности существует требование, чтобы работа с файлом происходила в эксклюзивном режиме, то есть в один момент времени с файлом может работать только одно приложение, в то время как доступ других будет заблокирован. &lt;br /&gt;
&lt;br /&gt;
Если мы реализуем код вида:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function ProcessFile(const string filename = '/var/db/mydb') {&lt;br /&gt;
    var dbfile = OpenDB(filename);&lt;br /&gt;
    var db = ReadDB(myfile);&lt;br /&gt;
    ProcessDB(db);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
...то произойдет следующее: сперва будет выполнена операция открытия файла и чтения его содержимого в функции &amp;lt;tt&amp;gt;ReadDB()&amp;lt;/tt&amp;gt;. После этого, будет выполнена операция &amp;lt;tt&amp;gt;ProcessDB()&amp;lt;/tt&amp;gt; в течение которой файл попрежнему будет открыт в эксклюзивном режиме и другие программы не смогут его использовать. На самом же деле, нашему приложению файл уже не нужен, так что его можно было бы и освободить. Для этого, мы должны применить дополнительный блок для изоляции переменной файла в пределах логического блока работы с файлом (который ограничен первыми двумя строками):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function ProcessFile(const string filename = '/var/db/mydb') {&lt;br /&gt;
    var db;&lt;br /&gt;
    {&lt;br /&gt;
        var dbfile = OpenDB(filename);&lt;br /&gt;
        var db = ReadDB(dbfile);&lt;br /&gt;
    }&lt;br /&gt;
    ProcessDB(db);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь, переменная ''dbfile'' будет существовать только в пределах блока, соответственно, при выходе из него, переменная будет удалена и доступ к файлу будет открыт. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Экспортирование функций ==&lt;br /&gt;
&lt;br /&gt;
Изначально, платформа и [[виртуальная машина Gide]] была разработана для свободного взаимодействия любого количества gide-совместимых языков программирования, которые могли бы свободно использоваться при написании программ. Причем, модуль написанный на одном языке программирования может импортировать другие модули, написанные на других языках. Одним из способов обеспечения совместимости языков является введение стандартного [[Gide IDL|языка описания интерфейсов]] модулей, который должны понимать языки, подразумевающие совместную работу в рамках системы.&lt;br /&gt;
&lt;br /&gt;
Написание совместимых программ накладывает определенные ограничения на именование идентификаторов классов и методов, поскольку в языках программирования имена методов как правило декорируются для обеспечения возможности перегрузки операторов и методов (см. ниже). Правила декорации имен в различных языках могут быть различными, соответственно пришлось бы их стандартизировать.&amp;lt;!--либо отказаться от декорации вообще, либо реализовывать механизм сопоставления декорированных и недекорированных имен. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В языке К++ введено специальное ключевое слово &amp;lt;tt&amp;gt;'''export'''&amp;lt;/tt&amp;gt;, которое позволяет отменить декорацию имени для отдельно взятой функции. Зная имя функции, ее можно будет вызывать из внешнего кода (имеется в виду внешний по отношению к gide код, например код некоторого плагина).&lt;br /&gt;
&lt;br /&gt;
При отмене декорирования функции, накладываются определенные ограничения на перегрузку функций:  попытка объявления двух экспортируемых методов с одинаковыми именами в рамках одного класса приведет к ошибке времени компиляции. Если экспортируется только один из этих методов, то это не является ошибкой.&lt;br /&gt;
&lt;br /&gt;
Экспортирование функций применяется при объявлении точек входа в программу. Для программ, такой точкой является функция &amp;lt;tt&amp;gt;main()&amp;lt;/tt&amp;gt;: &lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
export function void main() {&lt;br /&gt;
    /* тело функции */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После загрузки программы в память, происходит поиск функции с именем &amp;quot;main&amp;quot; которой передается управление.&lt;br /&gt;
&lt;br /&gt;
== Перегрузка функций и операторов ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;red&amp;quot; size=&amp;quot;5&amp;quot;&amp;gt;'''Внимание!'''&amp;lt;/font&amp;gt; В текущей (второй) версии компилятора, данная опция еще не реализована. Это связано с отменой декорированияя имени метода информацией о параметрах. В качестве временного решения для реализации некоторого подобия перегруженных методов можно применить код эмуляции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
class MyClass {&lt;br /&gt;
    public operator MyClass + (x) {&lt;br /&gt;
        switch (x.className) {&lt;br /&gt;
            case 'std/int': { /* MyClass + int */ }&lt;br /&gt;
            case 'std/string': { /* MyClass + string */ }&lt;br /&gt;
            default: { /* MyClass + (x as MyClass) */ }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;3: Объявляется оператор &amp;lt;tt&amp;gt;+&amp;lt;/tt&amp;gt;, принимающий в качестве параметра динамическую переменную.&lt;br /&gt;
;4: В коде оператора реализуется множественный выбор на основании класса переданного фактического параметра&lt;br /&gt;
;5-7: Определяются возможные случаи для поддерживаемых типов. Первый будет вызываться, если в качестве параметра передана строковая переменная, второй — если это число. Выбор по умолчанию будет соответствовать общему случаю, когда передан неизвестный тип. Здесь можно либо попытаться выполнить операцию явного приведения типа к самому &amp;lt;tt&amp;gt;MyClass&amp;lt;/tt&amp;gt;, либо к одному из поддерживаемых; либо сразу бросить исключение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При разработаке интерфейсов классов, часто бывает необходимо предоставить возможность выполнения одних и тех же операций с различными типами исходных данных. Например, класс пользовательского хранилища данных должен обладать методами добавления (импортирования) элементов из стандартных хранилищ, вроде [[Стандартные типы данных#Массивы и списки|массивов и списков]]. Точно так же, он должен предоставлять интерфейс экспортирования данных в стандартные типы. Если интерфейс будет реализован с учетом только одного из типов, то теоретически программа все же сможет работать (поскольку, типы &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;list&amp;lt;/tt&amp;gt; являются взаимоприводимыми). На практике, эта операция может привести к значительным потерям производительности, связанным в основном с многократным копированием содержимого при операции приведения.&lt;br /&gt;
&lt;br /&gt;
Решение может заключаться в реализации нескольких методов для импортирования и экспортирования данных в определенных форматах:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
class MyCollection {&lt;br /&gt;
public:&lt;br /&gt;
    function Add(const x); //добавление одиночного элемента&lt;br /&gt;
    function AddArray(const array a); //добавление массива&lt;br /&gt;
    function AddList(const list l); //добавление списка&lt;br /&gt;
    function AddCSV(const string s, //добавление строки с разделителем&lt;br /&gt;
        const string separator = &amp;quot;,&amp;quot;);&lt;br /&gt;
    function Insert(const x, const int pos = 0); //вставка одиночного элемента&lt;br /&gt;
    function InsertArray(const array a, const int pos = 0); //вставка массива&lt;br /&gt;
    function InsertList(const list l, const int pos = 0); //вставка списка&lt;br /&gt;
    function InsertCSV(const string s, //вставка строки с разделителем&lt;br /&gt;
        const int pos = 0, const string separator = &amp;quot;,&amp;quot;);&lt;br /&gt;
    const function array ExportArray(); //экспортирование в массив&lt;br /&gt;
    const function list ExportList(); //экспортирование в список&lt;br /&gt;
    const function string ExportCSV(); //экспортирование в строку&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь, при использовании такого класса, программисту придется для каждой операции применять функцию, рассчитанную на данный тип параметров:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
var col = new MyCollection;&lt;br /&gt;
col.Add(&amp;quot;hello world&amp;quot;);&lt;br /&gt;
col.AddArray([1, 2, 3]);&lt;br /&gt;
var l = new list;&lt;br /&gt;
foreach (1 .. 10) { |x| l.push(x); };&lt;br /&gt;
col.AddList(l);&lt;br /&gt;
col.AddCSV(&amp;quot;волк,коза,капуста&amp;quot;);&lt;br /&gt;
//(код вставки и экспортирования пишется по аналогии)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С одной стороны, вроде бы код довольно читаем и красив, но с другой, — есть одна маленькая неприятность. Допустим, программист написал некоторую функцию или даже целый класс, которые часто работают с инстанциями контейнера &amp;lt;tt&amp;gt;MyCollection&amp;lt;/tt&amp;gt; и со списками, которые импортируются в контейнер. Предположим, что однажды было решено, для повышения быстродействия использовать массивы вместо списков. В коде интерфейсов это изменение может затронуть всего пару строк, в то время как код функции придется переписывать, заменяя все вызовы &amp;lt;tt&amp;gt;AddList()&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;InsertList()&amp;lt;/tt&amp;gt; на соответствующие им вызовы: &amp;lt;tt&amp;gt;AddArray()&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;InsertArray()&amp;lt;/tt&amp;gt;. Если в коде функции существует только одна сущность представленная списком, то достаточно сделать замену в тексте одной подстроки на другую. Но что если их много? Придется проходить код строка за строкой и проверять каждый вызов — благодатная почва для ошибок. Конечно, кому-то может показаться, что приведенный пример &amp;quot;притянут за уши&amp;quot;, однако такая проблема на самом деле имеет место, просто довольно сложно придумать навскидку подходящую ситуацию.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как вы уже могли догадаться, наиболее рациональное решение заключается в использовании ''перегруженных функций''. Перегруженными, называются функции или методы, которые имеют одинаковые имена, но различные списки параметров. Вот примеры перегруженных функций:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function Hello(int x);                   //1&lt;br /&gt;
function Hello(int x, real y);           //2&lt;br /&gt;
function Hello(string x = &amp;quot;the value&amp;quot;);  //3&lt;br /&gt;
function Hello(array a, list l, hash h); //4&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Все вышеприведенные функции могут быть с успехом объявлены в одной области: либо как глобальные функции, либо в пределах класса — как методы. Как видите, имена всех функций одинаковы, а отличаются они лишь списком параметров. Отличия могут быть как в количестве параметров, так и в их типах.&lt;br /&gt;
&lt;br /&gt;
При вызове такого метода, первым делом проверяется количество фактически передаваемых параметров и их типы. На основании этой информации компилятор решает, какой же из методов необходимо использовать в каждом конкретном случае. Если, к примеру, пользователь вызвал функцию как &amp;lt;tt&amp;gt;Hello(1)&amp;lt;/tt&amp;gt;, то очевидно что имелся в виду метод номер 1. Вызов &amp;lt;tt&amp;gt;Hello(1, 2)&amp;lt;/tt&amp;gt; будет соответствовать методу номер 2. Несмотря на то что тип второго фактического параметра это &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;, компилятор все равно разберется, поскольку это единственный из методов, принимающий два параметра. Конечно, при генерации вызова будет выполнена операция приведения типа для второго параметра к типу &amp;lt;tt&amp;gt;real&amp;lt;/tt&amp;gt;. Наконец, вызов метода &amp;lt;tt&amp;gt;Hello()&amp;lt;/tt&amp;gt; без параметров будет соответствовать методу номер 3, поскольку в описании присутствует инициализатор.&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом перегружаются и операторы.&lt;br /&gt;
&lt;br /&gt;
=== Особенности применения ===&lt;br /&gt;
&lt;br /&gt;
При использовании перегрузки функций существует несколько ограничений, которые нужно иметь в виду при написании интерфейсов и программ. Перегружать функции и методы на основании типа их возвращаемого значения нельзя. Это невозможно по нескольким причинам. Во-первых, подобная практика привела бы к написанию плохого кода, который очень трудно отлаживать при возникновении каких либо проблем. Во-вторых, и это самое главное, существуют ситуации, когда компилятор не может решить какую из функций необходимо выбрать, например такое может случиться при использовании [[Переменные#Нетипированные (динамические) переменные|динамических переменных]]:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function int f() { return 1; }&lt;br /&gt;
function string f() { return &amp;quot;hello&amp;quot;; }&lt;br /&gt;
&lt;br /&gt;
function caller() {&lt;br /&gt;
    var x; //динамическая переменная&lt;br /&gt;
    x = f(); //какую из функций вызвать?!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
В приведенном примере выражение &amp;lt;tt&amp;gt;x = f();&amp;lt;/tt&amp;gt; не может быть корректно разобрано, поскольку компилятору не на что равняться при выборе конкретной реализации функции. Даже если бы мы использовали явное приведение типов в выражении (&amp;lt;tt&amp;gt;x = f() as int&amp;lt;/tt&amp;gt;), то компиляция все равно была бы неуспешной, поскольку с точки зрения компилятора эта операция должна выполняться уже после самого вызова функции.&lt;br /&gt;
&lt;br /&gt;
Та же проблема возникает и в случае передачи динамической переменной в перегруженную функцию:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function f(int x) { return x; }&lt;br /&gt;
function f(string s) { return x; }&lt;br /&gt;
&lt;br /&gt;
function caller() {&lt;br /&gt;
    var x; //динамическая переменная&lt;br /&gt;
    f(x); //какую из функций вызвать?!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Написание подобного кода ведет к ошибке времени компиляции. Однако, такая проблема возникает только при одинаковом количестве неинициализированных параметров в перегруженных функциях. &lt;br /&gt;
&lt;br /&gt;
В случае с операторами, в неоднозначной ситуации будет генерироваться динамический код. Поскольку операторы имеют строго определенные списки параметров и как правило принимают всего один или два параметра, то это оправдано. В случае с функциями, пришлось бы генерировать динамический код, даже если 9 из 10 параметров статические, а 1 динамический (который все и портит). Ограничение на функции было введено намеренно, чтобы избежать большого количества динамического кода и как следствие — падения производительности. &lt;br /&gt;
&lt;br /&gt;
Разрешение вышеописанной ситуации для функций сводится к принудительной типизации переменной:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;kpp&amp;quot;&amp;gt;&lt;br /&gt;
function caller() {&lt;br /&gt;
    var x;&lt;br /&gt;
    f(x to int);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Применение перегрузки в расширениях ===&lt;br /&gt;
&lt;br /&gt;
Еще одним аргументом в пользу перегруженных методов, является возможность их применения в [[Классы и объекты#Расширения|расширениях]]. Например, программист может расширить функциональность некотрого системного класса, добавляя возможность передачи своего класса в качестве аргумента методам. При этом, он добавляет метод, имеющий то же имя (но другой набор параметров), что и метод, существующий в расширяемом классе. &lt;br /&gt;
&lt;br /&gt;
При расширении сторонних классов следует быть особенно внимательным и не забывать, что правила декорирования имен методов в других языках могут быть различными, либо декорирование может не применяться вовсе. К примеру, в случае [[Стандартная библиотека Gide|стандартной библиотеки]], применять расширения можно смело, но нельзя их помечать как &amp;lt;tt&amp;gt;'''export'''&amp;lt;/tt&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Kamillen</name></author>	</entry>

	<entry>
		<id>http://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</id>
		<title>Стандартные типы данных</title>
		<link rel="alternate" type="text/html" href="http://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"/>
				<updated>2009-12-15T17:01:42Z</updated>
		
		<summary type="html">&lt;p&gt;Kamillen: /* Применение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;В этой главе будут рассмотрены базовые типы данных, применяющиеся в языке К++. Большинство из них объявлены в стандартной библиотеке Gide, однако некоторые, например интервалы, описываются в системной библиотеке самого языка К++. Еще раз напомним Читателю, что в языке К++, стандартные типы данных не являются встроенными. Конечно, компилятор опирается на них при генерации кода, но это совершенно не означает, что их нужно воспринимать как что-то единожды определенное и неизменное. Как уже было показано в книге, как с точки зрения компилятора так и самой виртуальной машины, эти классы  ничем не отличаются от обычных, пользовательских классов, когда дело касается их использования на высоком уровне. Если забежать вперед, то можно отметить, что на низком уровне они реализованы на языке C++ (из соображений производительности) и их интерфейсы представлены в стандартной библиотеке. Тем не менее, существует возможность их дополнения программистом-пользователем, что и было проделано в главах, посвященных [[Классы и объекты#Расширения|расширениям]]. Разумеется, от этих классов возможно наследовать собственные классы, точно так же, как и от любых других. В этой главе мы рассмотрим стандартные типы данных с точки зрения их применения и укажем некоторые свойства которые не были упомянуты в ходе повествования.&lt;br /&gt;
&lt;br /&gt;
== Целые числа ==&lt;br /&gt;
&lt;br /&gt;
Основой всей арифметики являются числа. Для представления чисел в программах применяются классы, объекты которых выступают как хранилища значений. В стандартной библиотеке, это класс &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;, умеющий хранить числа в диапазоне от -9223372036854775808 до 9223372036854775807 (64 битное целое со знаком).&lt;br /&gt;
&lt;br /&gt;
Класс поддерживает все необходимые арифметические операции, а так же имеет операторы преобразования в строку и из строки. Однако, следует быть осторожным и не полагаться полностью на автоматическое приведение типов. В некоторых случаях, можно получить не те результаты, что ожидает программист. Например:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var i = 1;&lt;br /&gt;
var j = &amp;quot;2&amp;quot;;&lt;br /&gt;
puts(i + j);&lt;br /&gt;
puts(j + i);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В приведенном выше примере, объявлены две переменных: целочисленная ''i'' и переменная ''j'', содержащая строку &amp;quot;2&amp;quot;. При вычислении первой суммы происходит следующее: компилятор &amp;quot;видит&amp;quot;, что необходимо посчитать сумму переменных ''i'' и ''j'', которые для нас являются представлениями чисел 1 и 2. Но с точки зрения компилятора, это всего лишь две инстанции классов &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
Разбор выражения производится слева-направо, так же как его считает человек. Компилятор пытается найти в классе &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; оператор &amp;lt;tt&amp;gt;+&amp;lt;/tt&amp;gt; с типом второго операнда, то есть &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;. Такого оператора в классе нет. Тогда компилятор смотрит, существует ли в классе &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt; [[Типы операторов#Операторы приведения типов|оператор приведения]] к типу &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;, либо к другому типу, для которого в классе &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; объявлен оператор &amp;lt;tt&amp;gt;+&amp;lt;/tt&amp;gt;. Такой оператор находится, соответственно компилятор генерирует вызов оператора приведения для переменной ''j'', а затем уже выполняется сам оператор &amp;lt;tt&amp;gt;+&amp;lt;/tt&amp;gt;. Поэтому, сложение происходит численно, так как и ожидалось. Результат суммы будет соответствовать числу 3:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
i + j = 1 + (&amp;quot;2&amp;quot; as int) = 1 + 2 = 3&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Во втором случае, все происходит с точностью наоборот: первым операндом является строка, то есть инстанция класса &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;. В этом классе так же объявлен оператор &amp;lt;tt&amp;gt;+&amp;lt;/tt&amp;gt;, но принимающий  в качестве второго операнда строку. Этот оператор используется для конкатенации двух строк, при которой вторая строка дописывается в конец первой, то есть:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s1 = &amp;quot;hello&amp;quot;;&lt;br /&gt;
var s2 = &amp;quot;world&amp;quot;;&lt;br /&gt;
puts(s1 + s2); //результат: &amp;quot;hello&amp;quot; + &amp;quot;world&amp;quot; = &amp;quot;helloworld&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В нашей сумме, вторым операндом является инстанция класса &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;, которая по вышеописанным правилам, будет приведена к типу &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;. Таким образом, операция суммы будет приведена к операции конкатенации строк &amp;quot;2&amp;quot; и &amp;quot;1&amp;quot;, то есть результат выражения будет &amp;quot;21&amp;quot;:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
j + i = &amp;quot;2&amp;quot; + (1 as string) = &amp;quot;2&amp;quot; + &amp;quot;1&amp;quot; = &amp;quot;21&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Приведенная проблема особенно опасна при использовании [[Переменные#Нетипированные (динамические) переменные|динамических переменных]], поскольку в одном случае может произойти одна операция, а в другом другая. Поэтому, использовать динамические переменные следует очень осторожно. Если у вас нет уверенности относительно типа передаваемой переменной, лучше подстраховаться и произвести операцию явного преобразования к интересующему вас типу.&lt;br /&gt;
&lt;br /&gt;
== Числа с плавающей точкой ==&lt;br /&gt;
&lt;br /&gt;
Для проведения сложных математических рассчетов, одних целых чисел недостаточно. Для представления действительных чисел, или чисел с плавающей точкой (запятой), как их называют в информатике, в стандартной библиотеке создан класс &amp;lt;tt&amp;gt;real&amp;lt;/tt&amp;gt;, который на низком уровне представлен типом двойной точности (double). &lt;br /&gt;
&lt;br /&gt;
В отличие от класса &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;, класс &amp;lt;tt&amp;gt;real&amp;lt;/tt&amp;gt; корректно обрабатывается в сочетании с целочисленными классами. Например, следующие выражения дадут одинаковые результаты:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var i = 3 + 0.1415&lt;br /&gt;
var j = 0.1415 + 3&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате, обе переменных будут иметь тип &amp;lt;tt&amp;gt;real&amp;lt;/tt&amp;gt; и значение &amp;lt;!--примерно --&amp;gt;3,1415. Это достигается тем, что в классе &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; есть специальные версии арифметических операторов, которые принимают в качестве параметра объекты класса &amp;lt;tt&amp;gt;real&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Логический тип ==&lt;br /&gt;
&lt;br /&gt;
В некоторых случаях в программе бывает необходимо сохранять логическое значение. Это может потребоваться в алгоритмах, где такое значение используется в качестве флага, либо в свойствах объектов, которые подразумевают наличие или отсутствие некоторого признака. Это осуществляется с помощью булевых переменных и псевдотипа &amp;lt;tt&amp;gt;bool&amp;lt;/tt&amp;gt;. Переменные булевого типа могут иметь только два значения: &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;, соответствующее истине и &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;, соответствующее лжи.&lt;br /&gt;
&lt;br /&gt;
К переменным булевого типа могут приводиться значения более сложных выражений, например когда такое выражение находится в условии цикла. Правила преобразования значений выражений были описаны при рассмотрении [[Основные синтаксические конструкции#Оператор if|условного оператора &amp;lt;tt&amp;gt;'''if'''&amp;lt;/tt&amp;gt;]].&lt;br /&gt;
&lt;br /&gt;
'''Примечание:''' псевдотипом &amp;lt;tt&amp;gt;bool&amp;lt;/tt&amp;gt; назван потому, что он не является классом в полной мере.  Это абстракция, которая была введена в язык К++ для преодоления некоторых сложностей при проектировании языка. Переменные такого класса обрабатываются на уровне самого К++; с точки зрения Gide такого класса не существует (вернее, существуют специальные объекты &amp;lt;tt&amp;gt;'''@true'''&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;'''@false'''&amp;lt;/tt&amp;gt;, но это тема, требующая отдельного обсуждения).&lt;br /&gt;
&lt;br /&gt;
Существует несколько ограничений, которые важно понимать. Первое ограничение заключается в том, что от этого типа нельзя наследовать собственные классы. Второе ограничение проявляется в том, что переменные типа &amp;lt;tt&amp;gt;bool&amp;lt;/tt&amp;gt; обрабрабатываются особым образом. Если в выражении присутствует переменная булевого типа, то операторы отношения работают немного по другому. Например, следующее выражение будет трактовано как истина:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
if (0 == true)&lt;br /&gt;
   //...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Условие сработает потому что 0 представляет собой действительный, существующий объект, стало быть он истинен (об этом уже было написано). Вот другой пример, иллюстрирующий ту же проблему:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var MyClass c = new MyClass;&lt;br /&gt;
if (c == true)&lt;br /&gt;
   //...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подобное условие так же сработает (будет считаться истинным), поскольку оператор &amp;lt;tt&amp;gt;==&amp;lt;/tt&amp;gt; в данном случае обрабаытвается самим компилятором. Конечно, приведенные примеры являются довольно странными с точки зрения обычного кода, но их все же следует иметь в виду при написании программ.&lt;br /&gt;
&lt;br /&gt;
== Строки ==&lt;br /&gt;
&lt;br /&gt;
Для хранения и обработки строковых данных используется класс &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;. Он объявлен в [[Стандартная библиотека Gide|стандартной библиотеке Gide]] и дополнен с помощью [[Классы и объекты#Расширения|расширений]] в системной библиотеке К++. &lt;br /&gt;
&lt;br /&gt;
В языке К++ предусмотрены целых три синтаксиса для объявления строковых констант, которые в конечном счете представляются одним и тем же классом &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Строки, заключенные в двойные кавычки обрабатывают все escape последовательности, а так же позволяют использовать т. н. вставки, или подстановки — кусочки кода, которые вставляются непосредственно в тело строки и при работе программы заменяются на значение, возвращаемое этим кодом (см. ниже). Обрабатываются следующие последовательности:&lt;br /&gt;
::{|&lt;br /&gt;
 ! Последовательность  || Значение&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''\n'''&amp;lt;/tt&amp;gt;   || перенос строки&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''\r'''&amp;lt;/tt&amp;gt;   || возврат каретки&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''\t'''&amp;lt;/tt&amp;gt;   || символ табуляции&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''\&amp;quot;'''&amp;lt;/tt&amp;gt;   || символ двойной кавычки&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''\\'''&amp;lt;/tt&amp;gt;   || символ обратного слеша&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''#{ }'''&amp;lt;/tt&amp;gt; || подстановка выражения&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
При этом, при обработке строки, найденные последовательности заменяются на соответствующий им управляющий символ. Например, последовательность &amp;lt;tt&amp;gt;'''\n'''&amp;lt;/tt&amp;gt; будет заменена на один символ перевода строки (ASCII код 13).&lt;br /&gt;
&lt;br /&gt;
Подстановки это очень удобный способ совмещать в одном выражении строку которую необходимо вывести и выражения, значение которых необходимо добавить к выводу. Получается что подстановки это что-то среднее между простой строкой и строкой для форматируемого вывода. Подстановка начинается с пары символов &amp;lt;tt&amp;gt;'''#{'''&amp;lt;/tt&amp;gt;, которые должны идти друг за другом, без пробелов. То что идет дальше, воспринимается компилятором как выражение, как если бы оно было записано просто в коде, вне строки. Завершает подстановку соответствующая ей закрывающая фигурная скобка.&lt;br /&gt;
&lt;br /&gt;
Приведем пример использования подстановок:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s1 = &amp;quot;сумма чисел 2 и 3 равна #{2 + 3}.&amp;quot;;&lt;br /&gt;
print(&amp;quot;s1 = #{s1}\n&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При разборе выражения компилятор преобразовывает данное выражение в сумму, представляющую из себя последовательность строковых констант и выражений:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s1 = &amp;quot;сумма чисел 2 и 3 равна &amp;quot; + (2 + 3) + &amp;quot;.&amp;quot;;&lt;br /&gt;
print(&amp;quot;s1 = &amp;quot; + (s1) + &amp;quot;\n&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате выполнения этого кода, в выводе появится текст (курсор будет переведен на следующую строку):&lt;br /&gt;
 s1 = сумма чисел 2 и 3 равна 5.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Подстановки даже можно делать вложенными, например так:&lt;br /&gt;
&amp;lt;!--&amp;lt;source lang=kpp&amp;gt;--&amp;gt;&lt;br /&gt;
 var x = [ 1, 2, 3, 4, 5 ];&lt;br /&gt;
 var s = &amp;quot;some weird stuff: #{ x.join(', ') { |idx,elem| &amp;quot;#{idx} = #{elem}&amp;quot;; } }\n&amp;quot;;&lt;br /&gt;
&amp;lt;!--&amp;lt;/source&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если требуется работать со строкой, которая содержит escape последовательности (или подстановки), которые не требуется обрабатывать то следует применять строки, заключенные в одинарные кавычки:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s1 = 'в таких строках \n \r \t и #{подстановки} не обрабатываются';&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Исключением из правила являются последовательности '''&amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;\'&amp;lt;/nowiki&amp;gt;&amp;lt;/tt&amp;gt;''' и &amp;lt;tt&amp;gt;'''\\'''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Третьим способом объявить строку является применение синтаксиса кратких, &amp;quot;однословных&amp;quot; строк. Такие строки начинаются со знака двоеточия, следом за которым могут идти те же символы, что возможны при написании идентификаторов, то есть: латинские буквы, цифры и знак подчеркивания, притом что первой после двоеточия обязательно идет либо буква, либо подчеркивание. Такие строки могут быть особенно полезны при записи ключей хеша (как будет показано ниже), либо просто в тех местах где в качестве строки передается одно слово:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var h = { :sunday = 1, :monday = 2, :tuesday = 3 };&lt;br /&gt;
var x = my_function(:hello);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Так же, класс &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt; предоставляет пользователю широкий спектр различных методов, которые могут использоваться для работы со строками.&lt;br /&gt;
&lt;br /&gt;
=== Длина строки ===&lt;br /&gt;
&lt;br /&gt;
Для определения длины строки используется метод &amp;lt;tt&amp;gt;length()&amp;lt;/tt&amp;gt;, который возвращает число символов в строке. Так же, существует метод &amp;lt;tt&amp;gt;empty()&amp;lt;/tt&amp;gt; который возвращает истину если строка пуста и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
=== Выделение подстрок ===&lt;br /&gt;
&lt;br /&gt;
Для выделения из исходной строки подстрок, разделенных символом или несколькими символвами разделителями, используется метод &amp;lt;tt&amp;gt;split()&amp;lt;/tt&amp;gt;. В качестве параметра, этот метод принимает строку, либо [[Введение, или краткий обзор#Регулярные выражения|регулярное выражение]], соответствующие разделителю. Возвращает метод уже массив из полученных подстрок.&lt;br /&gt;
&lt;br /&gt;
В качестве примера приведем два способа обработки URL строки и выделения из нее отдельных частей:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var url = &amp;quot;http://example.com?key1=value1&amp;amp;key2=value2&amp;amp;key3=value3&amp;quot;;&lt;br /&gt;
var qm = url.find(&amp;quot;?&amp;quot;);&lt;br /&gt;
var args = url.substr(qm, url.length());&lt;br /&gt;
var pairs = args.split('&amp;amp;');&lt;br /&gt;
var options = new hash;&lt;br /&gt;
pairs.each() { |pair| &lt;br /&gt;
    var kv = pair.split('='); &lt;br /&gt;
    options[kv[0]] = kv[1]; &lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С помощью метода &amp;lt;tt&amp;gt;find()&amp;lt;/tt&amp;gt; мы находим позицию символа вопроса в исходной строке, который разделяет адрес на собственно адрес ресурса и на строку параметров запроса. Строка параметров состоит из множества пар ''значение''&amp;lt;tt&amp;gt;=&amp;lt;/tt&amp;gt;''результат'', отделенных друг от друга символом амперсанда. Выделив подстроку запроса, мы разделяем ее на подстроки с помощью метода &amp;lt;tt&amp;gt;split()&amp;lt;/tt&amp;gt;, указывая амперсанд в качестве разделителя. Полученный массив подстрок мы сохраняем в переменной ''pairs''. Теперь, мы заводим [[Стандартные типы данных#Хеши|хеш]] ''options'' в который будем записывать параметры запроса, по мере его обработки. Выполняя перебор по всем парам, мы опять разделяем их на две части, на этот раз по символу равенства. Последним действием, полученная пара значение-результат сохраняется в хеше. &lt;br /&gt;
&lt;br /&gt;
Несмотря на то, что вышеприведенный код работает, он является не очень эффективным. В процессе его работы создается много промежуточных переменных и может задействоваться большое количество памяти (при длинных строках запросов). При задачах обработки строк выгодно использовать регулярные выражения. Они позволяют относительно легко производить обработку строк; выполнять поиск и замену по некоторому шаблону. Приведем другой, более элегантный способ решения той же самой задачи, но уже с использованием регулярных выражений:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var options = new hash;&lt;br /&gt;
if (url =~ `\?(.*)$`) {&lt;br /&gt;
    //разделяем строку на пары значение=результат&lt;br /&gt;
    var pairs = $[0].value.split('&amp;amp;');&lt;br /&gt;
    //обрабатываем пары по очереди&lt;br /&gt;
    pairs.each() { |pair|&lt;br /&gt;
        if (pair =~ `^([\w\d]+)=([\w\d]+)$`)&lt;br /&gt;
            options[$[1].value] = $[2].value;&lt;br /&gt;
    };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оператор &amp;lt;tt&amp;gt;=~&amp;lt;/tt&amp;gt; служит для связи исходной строки с регулярным выражением. В результате, в специальный массив &amp;lt;tt&amp;gt;$[]&amp;lt;/tt&amp;gt; будут помещены все совпадения. Элемент с индексом 0 будет соответствовать всей совпавшей подстроке, в остальных элементах будут находиться совпадения, соответствующие подвыражениям в скобках. &lt;br /&gt;
&lt;br /&gt;
'''Примечание 1:''' элементы спецмассива &amp;lt;tt&amp;gt;$[]&amp;lt;/tt&amp;gt; представляют собой не сами подстроки, а объекты-совпадения, класса &amp;lt;tt&amp;gt;regexp_match&amp;lt;/tt&amp;gt;. В этих объектах содержится дополнительная информация о совпадении, например: &lt;br /&gt;
* индексы левой и правой границы (свойства &amp;lt;tt&amp;gt;left&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;right&amp;lt;/tt&amp;gt;)&lt;br /&gt;
* длина подстроки (свойство &amp;lt;tt&amp;gt;length&amp;lt;/tt&amp;gt;)&lt;br /&gt;
* и, естественно, сама подстрока (свойство &amp;lt;tt&amp;gt;value&amp;lt;/tt&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Получить доступ к подстроке так же можно, выполнив явное приведение объекта совпадения к типу &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;. Таким образом, следующие строки функционально эквивалентны:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var substr1 = $[1].value;&lt;br /&gt;
var substr2 = $[1] as string;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Примечание 2:'''  в принципе, можно было и не объявлять отдельную переменную ''pairs''. В книге это было сделано для большей читаемости кода, однако на практике может применяться и более лаконичная форма, которая, тем не менее, остается довольно удобной к восприятию:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
if (url =~ `\?(.*)$`) {&lt;br /&gt;
    $[1].value.split('&amp;amp;').each() { |pair|&lt;br /&gt;
        if (pair =~ `^([\w\d]+)=([\w\d]+)$`)&lt;br /&gt;
            options[$[1].value] = $[2].value;&lt;br /&gt;
    };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При желании, можно еще больше упростить код, используя версию оператора &amp;lt;tt&amp;gt;[]&amp;lt;/tt&amp;gt;, принимающую регулярное выражение в качестве параметра и возвращающее соответствующую ему подстроку:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
url[`\?(.*)$`].value.split('&amp;amp;').each() { |pair|&lt;br /&gt;
    if (pair =~ `^([\w\d]+)=([\w\d]+)$`)&lt;br /&gt;
        options[$[1].value] = $[2].value;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако, в этом случае, при отсутствии требуемой подстроки в исходной переменной ''url'', будет сгенерировано исключение и это следует корректно обрабатывать.&lt;br /&gt;
&lt;br /&gt;
=== Объединение строк (конкатенация) ===&lt;br /&gt;
&lt;br /&gt;
Конкатенация строк осуществляется с помощью обычных арифметических операторов (при этом, приоритеты операций остаются теми же). В зависимости от оператора, действие будет производиться над самим объектом или над его копией:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s1 = &amp;quot;hello&amp;quot;;&lt;br /&gt;
var s2 = &amp;quot;world&amp;quot;;&lt;br /&gt;
var s3 = s1 + ' ' + s2; // s3 = &amp;quot;hello world&amp;quot;, s1 и s2 те же&lt;br /&gt;
s1 = (s3 += &amp;quot;!&amp;quot;);       // s1 = s3 = &amp;quot;hello world!&amp;quot;&lt;br /&gt;
s2 *= 2;                // s2 = worldworld&lt;br /&gt;
s3 = s1 + '!' * 2;      // s3 = &amp;quot;hello world!!!&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на две последние строки. Оператор &amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;, выполняемый над строкой действительно &amp;quot;умножает&amp;quot; ее, повторяя заданное количество раз.&lt;br /&gt;
&lt;br /&gt;
=== Операторы индексного доступа ===&lt;br /&gt;
&lt;br /&gt;
Для доступа к отдельным символам строки может использоваться оператор индексного доступа &amp;lt;tt&amp;gt;[]&amp;lt;/tt&amp;gt;. При этом, строка представляется как [[Стандартные типы данных#Массивы и списки|массив]], где каждый элемент соответствует '''номеру символа''' в unicode:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var str = &amp;quot;hello world&amp;quot;;&lt;br /&gt;
var ord = str[0]; // ord = 104&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Можно совершать и обратную операцию, а именно присваивать по некоторому индексу числовое значение. Тогда в строке будет стоять символ, соответствующий указанному числу:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
str[0] = 72;     // str = &amp;quot;Hello world&amp;quot;&lt;br /&gt;
str[6] = 'W'[0]; // str = &amp;quot;Hello World&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Существует дополнительный синтаксис, специально предназначенный для получения номера некоторого символа. Он осуществляется записью символа диез (&amp;quot;решетка&amp;quot;), с последующим добавлением исследуемого символа. Таким образом, вторую строку из предыдущего примера можно записать так:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
str[6] = #W;     // str = &amp;quot;Hello World&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот синтаксис можно применять даже для управляющих символов вроде '''\n''' и даже для любого символа Unicode:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var n = #\n;     // n будет содержать число 13&lt;br /&gt;
var f = #Ф;      // код, соответствующий русской заглавной букве Ф&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же требуется получить подстроку исходной строки на основании индексов символов, нужно применять [[Стандартные типы данных#Интервалы|интервалы]]:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s2 = str[0 .. 4]; // s2 = &amp;quot;Hello&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Возможно так же использовать оператор индексного доступа для присвоения значения отдельным подстрокам. Это осуществляется с помощью оператора &amp;lt;tt&amp;gt;[]=&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var greeting = &amp;quot;Welcome to the real world, Neo.&amp;quot;;&lt;br /&gt;
greeting['real world'] = 'matrix'; // Welcome to the matrix, Neo.&lt;br /&gt;
greeting[15 .. 20] = 'Deeptown';   // Welcome to the Deeptown, Neo.&lt;br /&gt;
greeting[-1] = #!;                 // Welcome to the Deeptown, Neo!&lt;br /&gt;
greeting[`\sNeo.$`] = 'Leonid!';   // Welcome to the Deeptown, Leonid!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В примере выше, применены четыре способа индексного доступа для изменения строки, соответствующие четырем различным операторам (точнее, четырем версиям перегруженного оператора). В первом случае используется доступ по подстроке. При этом подстрока будет &amp;quot;вырезана&amp;quot; из строки, а на ее место будет вставлена присваиваемая строка. Таким образом, эта операция не зависит от размера искомой и заменяемой подстрок (если заменяемая строка длиннее, исходная строка будет &amp;quot;раздвинута&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Во втором случае осуществляется доступ по интервалу. Символы с индексами в диапазоне 15-20 соответствуют слову &amp;quot;matrix&amp;quot;. Замена опять же производится по правилу &amp;quot;вырезания&amp;quot; подстроки с последующим &amp;quot;склеиванием&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
В третьем случае, используются отрицательные индексы, для указания того, что отсчитывать символы нужно с конца строки. Так, -1 соответствует последнему символу, -2 второму с конца и т. д.&lt;br /&gt;
&lt;br /&gt;
Наконец, в последней замене применяется [[Введение, или краткий обзор#Регулярные выражения|регулярное выражение]], для поиска подстроки.&lt;br /&gt;
&lt;br /&gt;
'''Примечание:''' Методы замены через оператор &amp;lt;tt&amp;gt;[]=&amp;lt;/tt&amp;gt; на деле являются обертками к соответствующим реализациям метода &amp;lt;tt&amp;gt;replace_all()&amp;lt;/tt&amp;gt;. При этом, будут производиться замены всех включений подстроки в исходную строку (разумеется, кроме случаев ин тервала и одиночного индекса). Если вы хотите заменить только одно включение, — используйте методы &amp;lt;tt&amp;gt;replace()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Интервалы == &lt;br /&gt;
&lt;br /&gt;
В некоторых случаях бывает необходимо передать в качестве параметра не отдельное значение а диапазон. Для указания диапазона обычно достаточно указать только его границы. Это может осуществляться с помощью класса &amp;lt;tt&amp;gt;interval&amp;lt;/tt&amp;gt;. К++ предоставляет специальный синтаксис для указания интервалов с помощью оператора &amp;quot;&amp;lt;tt&amp;gt;..&amp;lt;/tt&amp;gt;&amp;quot;; как уже было показано выше, это может применяться для выборки подстрок, соответствующих заданному диапазону индексов, либо подмассивов по тому же принципу:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s = &amp;quot;abcdef&amp;quot;[0..2];      // &amp;quot;abc&amp;quot;&lt;br /&gt;
var a = [1, 2, 3, 4][1..-1]; // [2, 3, 4]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вообще, интервалы могут быть не только численными. В качестве объектов, формирующих интервал, могут выступать любые два объекта, имеющие одинаковые типы:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var left = MyClass.Create(5), right = MyClass.Create(10);&lt;br /&gt;
var i = interval.create(left, right);&lt;br /&gt;
var alpahbet = 'a' .. 'z';&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Интервалы могут использоваться как виртуальные массивы, то есть к ним можно обращаться как к массиву, запрашивая некоторый элемент по индексу. И он будет возвращен, как будто действительно хранится в массиве (на сама деле, он создается в момент обращения по некоторому известному закону):&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var numbers = 1 .. 100;&lt;br /&gt;
var alpahbet = 'a' .. 'z';&lt;br /&gt;
var x = numbers[50];  // x = 1 + (50-1) = 50.&lt;br /&gt;
var y = alphabet[10]; // y = (#a + (10-1)).char = 'k'&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Преимуществом такого подхода является то, что не нужно хранить весь массив в памяти. Зная закон изменения элементов, можно получить любой элемент, зная базу (одну из границ) и индекс элемента.&lt;br /&gt;
&lt;br /&gt;
Подобно массивам, интервалы так же обладают методом &amp;lt;tt&amp;gt;each()&amp;lt;/tt&amp;gt;, позволяющем проходить по всему массиву,  выполняя некоторый блок с параметром текущего элемента массива:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s = 0; //здесь будет сумма&lt;br /&gt;
var numbers = 1 .. 100;&lt;br /&gt;
numbers.each() { |x| s += x; };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для выяснения принадлежности некоторого объекта к интервалу могут применяться метод &amp;lt;tt&amp;gt;contains()&amp;lt;/tt&amp;gt; или соответствующий ему оператор &amp;lt;tt&amp;gt;'''in'''&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var numbers = 1 .. 100;&lt;br /&gt;
var lowercase = 'a' .. 'z';&lt;br /&gt;
var x = numbers.contains(75) ? &amp;quot;yes&amp;quot; : &amp;quot;no&amp;quot;;    //x = &amp;quot;yes&amp;quot;&lt;br /&gt;
var y = 'X' in lowercase ? &amp;quot;yes&amp;quot; : &amp;quot;no&amp;quot;; //y = &amp;quot;no&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для создания интервалов на базе собственных классов необходимо перегрузить конструктор, а так же соответствующие методы, своими реализациями.&lt;br /&gt;
&lt;br /&gt;
== Массивы и списки ==&lt;br /&gt;
&lt;br /&gt;
Для хранения наборов объектов применяются массивы и списки. И те и другие являются контейнерами, то есть, их объекты способны хранить в себе другие объекты и обладают соответствующими методами для добавления в них элементов и их извлечения. Разница между массивом и списком заключается в том, что массив хранит элементы в одной цельной области памяти, в то время как список организует их в виде цепочки связанных друг с другом элементов:&lt;br /&gt;
&lt;br /&gt;
[[Изображение:Array_vs_list.png|center]]&lt;br /&gt;
&lt;br /&gt;
У каждого из этих контейнеров есть свои преимущества и недостатки. Постараемся рассмотреть основные операции, осуществляемые с контейнерами и выяснить, в каких случаях предпочтительнее использовать массив, а в каких список.&lt;br /&gt;
&lt;br /&gt;
=== Доступ к данным ===&lt;br /&gt;
&lt;br /&gt;
Массивы обладают высокой скоростью доступа к информации, поскольку все данные находятся в одном месте и структурированы. То есть, доступ есть сразу ко всем элементам, поэтому не приходится двигаться вдоль массива для поиска нужного элемента. Списки работают несколко по другому. Для того, чтобы обратиться к некоторому элементу списка, необходимо пройти всю цепочку от начала до требуемого элемента (см. рисунок выше). Поэтому, списки значительно уступают по скорости доступа массивам. В случаях, когда содержимое контейнера меняется относительно редко, но присутствует большое количество обращений к данным, следует применять массивы.&lt;br /&gt;
&lt;br /&gt;
=== Добавление элементов ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим операцию добавления элементов в список. Предположим, что нам надо добавить элемент N в список, между вторым и третьим его элементами. Для выполнения этой операции, нам необходимо всего лишь &amp;quot;разорвать&amp;quot; цепочку в нужном месте и изменить указатель у предыдущего элемента X&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; так, чтобы он указывал на новый элемент N. Для того, чтобы продолжить цепочку, элемент N должен указывать на следующий элемент списка, то есть на X&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt;:&lt;br /&gt;
[[Изображение:List insert.png|center]]&lt;br /&gt;
&lt;br /&gt;
Операция добавления к началу или к хвосту списка так же не составляет труда, и сводится все к той же работе с указателями. Удаление элементов производится обратным образом: элемент удаляется, а указатель предыдущего элемента меняется так, чтобы он указывал на последующий за удаляемым элемент.&lt;br /&gt;
&lt;br /&gt;
В случае массивов все усложняется. Как уже было сказано выше, массивы хранят элементы в монолитных областях памяти, следовательно, чтобы добавить или удалить элемент, приходится задействовать весь массив. Если места в текущей области памяти, занятой массивом недостаточно, чтобы вместить еще один элемент, то произойдет выделение нового блока памяти, с последующим копированием всех существующих элементов массисва в новый блок. &lt;br /&gt;
&lt;br /&gt;
Операция выделения производится &amp;quot;с запасом&amp;quot;, то есть, размер нового блока будет больше, чем размер занимаемый всеми элементами, причем зависимость тут экспоненциальная: чем больеше будет добавляться элементов в массив, тем больше будет размер выделяемого блока памяти. Это делается для того, чтобы минимизировать риск полного копирования. Тем не менее, такое может случиться (а при операции вставки — случается обязательно), что в итоге может привести к значительной задержке.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим тот же случай, что и в случае со списком, а именно, операцию вставки нового элемента, между вторым и третьим элементами массива. Для выполнения этой операции создается новый массив, с размером, достаточным для помещения всех предыдущих элементов, а так же нового элемента. Далее, происходит копирование всех элементов на соответствующие места:&lt;br /&gt;
[[Изображение:Array insert.png|center]]&lt;br /&gt;
&lt;br /&gt;
Это случай неудачного стечения обстоятельств, при котором происходит копирование большого количества информации, что естественным образом сказывается на призводительности всей операции. Из этого можно сделать вывод, что массивы следует применять тогда, когда операции вставки или добавления элементов происходят редко. В случаях, когда необходимо организовывать стеки или очереди, где обращение по номеру элемента не требуется, наилучшим решением будет использование списков.&lt;br /&gt;
&lt;br /&gt;
=== Применение ===&lt;br /&gt;
&lt;br /&gt;
Разобравшись с философией работы массивов и списков, поняв разницу между ними и определившись с областями применения тех и других, мы переходим непосредственно к практике. Массивы и списки в языке К++ представляются классами &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;list&amp;lt;/tt&amp;gt; соответственно. Оба класса имеют очень похожие наборы методов, так что дважды изучать одно и то же не придется.&lt;br /&gt;
&lt;br /&gt;
Объявление массива реализуется с помощью специального синтаксиса, встроенного в язык К++. Это может осуществляться как в инициализаторе переменной, так и в любом другом выражении, вплоть до фактического параметра в коде вызова функции, или даже для указания значения по умолчанию для него. &lt;br /&gt;
&lt;br /&gt;
Для того, чтобы объявить массив, необходимо перечислить его элементы, заключив их в квадратные скобки и отделив друг от друга запятой:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var my_array = [1, 2, 3];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В качестве элементов массива могут быть указаны любые объекты: числа, строки, экземпляры классов, блоки и даже вложенные массивы с хешами — массиву все равно что хранить, поскольку все что он делает, это хранит ссылки на объекты:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var myobject = new MyClass;&lt;br /&gt;
var object_dump = [1, 3.14, 'hello', myobject, {|x| return x + 1;}, &lt;br /&gt;
    {:a =&amp;gt; 1, :b =&amp;gt; 2 }, [1,2,3], true, 10 .. 20];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Можно сказать, что массив представляет собой реализацию абстрактного упорядоченного хранилища данных с возможностью произвольного доступа.&lt;br /&gt;
&lt;br /&gt;
Специального синтаксиса для создания списков нет, поскольку большинство задач которые решают списки не предполагают инициализации значениями. Если же это все таки потребовалось, на помощь придут массивы. Вот небольшой пример того как можно занести в список значения из массива, создаваемого компилятором автоматически:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var my_list = new list;&lt;br /&gt;
[1, 2, 3].each() { |x| my_list.push(x); };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом примере показываются сразу два важных приема, которые являются одними из самых частых по использованию (если вы внимательно читали книгу, то наверняка это заметили). Метод &amp;lt;tt&amp;gt;each()&amp;lt;/tt&amp;gt; используется для перебора всего содержимого коллекции (массива, списка или интервала) и вызова некоторого блока, передавая ему на каждой итерации значение текущего элемента. Таким образом, в случае нашего массива, блок будет вызыван три раза, с параметрами, соответственно, 1, 2 и 3. Метод &amp;lt;tt&amp;gt;push()&amp;lt;/tt&amp;gt; присутствует как в массивах, так и в списках, и служит для добавления элемента в конец коллекции. Здесь он применяется для добавления текущего элемента массива в список. Получается, что по выполнении этой процедуры, список будет содержать в себе все элементы, присутствующие в массиве (в том же порядке).&lt;br /&gt;
&lt;br /&gt;
Существуют еще несколько методов, похожих на метод &amp;lt;tt&amp;gt;each()&amp;lt;/tt&amp;gt;, но отличающихся передаваемыми в блок параметрами. Например &amp;lt;tt&amp;gt;each_index()&amp;lt;/tt&amp;gt; вызывает блок, передавая ему текущий индекс, а метод &amp;lt;tt&amp;gt;each_pair()&amp;lt;/tt&amp;gt; передает ему текущий индекс и элемент. За неимением индекса, списки два последних метода не реализуют. Переделаем немного предыдущий пример и покажем использование этих методов:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var my_array = [1, 2, 3];&lt;br /&gt;
var my_list = new list;&lt;br /&gt;
my_array.each() { |x| my_list.push(x); };&lt;br /&gt;
&lt;br /&gt;
my_list.each() { |x| print(&amp;quot;#{x}\n&amp;quot;); };&lt;br /&gt;
my_array.each_pair() { |i, e| print(&amp;quot;my_array[#{i}] = #{e}\n&amp;quot;); };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Иногда бывает необходимо поместить элементы массива или списка в строку. Например, чтобы вывести их пользователю или, возможно, записать в файл некоторые настройки, подразумевающие список значений. Это может быть сделано с помощью метода &amp;lt;tt&amp;gt;join()&amp;lt;/tt&amp;gt; который возвращает строку с элементами, перемежая их некоторой строкой-разделителем. Разделитель может быть передан в качестве параметра; по умолчанию это строка &amp;quot;, &amp;quot; (запятая с пробелом):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var my_array = [1, 2, 3];&lt;br /&gt;
var my_list = new list;&lt;br /&gt;
my_array.each() { |x| my_list.push(x); };&lt;br /&gt;
&lt;br /&gt;
var s_list = '[' + my_list.join() + ']'; // s_list = &amp;quot;[1, 2, 3]&amp;quot;&lt;br /&gt;
//дальнейшая работа с s_list (например, вывод на экран)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это простой пример использования метода &amp;lt;tt&amp;gt;join&amp;lt;/tt&amp;gt;, при котором он используется для создания текстового представления содержимого списка. Для удобства и указания границ, добавляются квадратные скобки. Вот другой пример использования метода, на этот раз, для сохранения некоторого списка ресурсов которые используются приложением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
//подготавливаем список опций&lt;br /&gt;
var search_dirs = new list;&lt;br /&gt;
search_dirs.push('http://example.com/db/');&lt;br /&gt;
search_dirs.push('ftp://example.org/db/');&lt;br /&gt;
search_dirs.push('diss:/media/storage/~user1/');&lt;br /&gt;
&lt;br /&gt;
//открываем поток файла настроек&lt;br /&gt;
var config = stream.open('diss:/etc/sample.conf', stream.create | stream.write);&lt;br /&gt;
config.write('dbpath=' + search_dirs.join(';')); //записываем строку конфигурации&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как мы видим, сначала происходит заполнение списка значениями URL ресурсов, а затем полученный список записывается в ''поток''. Класс потока это абстракция, предоставляемая ядром Диптауна для доступа к различным ресурсам. Все что необходимо сделать чтобы использовать потоки, это создать инстанцию класса &amp;lt;tt&amp;gt;stream&amp;lt;/tt&amp;gt; с момощью конструктора &amp;lt;tt&amp;gt;create()&amp;lt;/tt&amp;gt;, указав требуемый URL потока и задать режим доступа к ресурсу (с помощью констант класса). При этом, на пользовательском уровне, требуется только писать в поток и читать из него; пользователь не должен беспокоиться, каким образом будет происходить реальная работа с данными на низком уровне — этим занимается ядро. Если же ядро не поддерживает конкретный тип потока — будет сгенерировано исключение.&lt;br /&gt;
&lt;br /&gt;
Для последуюезго разбора полученной строки, можно применять уже известный нам метод &amp;lt;tt&amp;gt;split()&amp;lt;/tt&amp;gt; и регулярные выражения.&lt;br /&gt;
&lt;br /&gt;
В заключение, приведем две реализации кода, вычисляющего первую сотню простых чисел. Одну реализацию мы напишем с использованием списков, а другую с помощью массивов. В нижеприведенном коде активно применяются циклы, так что, для лучшего понимания материала, Читателю предлагается забежать вперед и [[Основные синтаксические конструкции#Циклы|ознакомиться]] с ними. Итак, первая реализация:&lt;br /&gt;
&amp;lt;source lang=kpp line=1&amp;gt;&lt;br /&gt;
function GetPrimes() {&lt;br /&gt;
    var primes = new list;&lt;br /&gt;
    var x = 1;&lt;br /&gt;
    while (primes.size() &amp;lt; 100) {&lt;br /&gt;
        x++; //текущий кандидат&lt;br /&gt;
        var is_prime = true;&lt;br /&gt;
        for (var p = primes.begin(); p != primes.end(); ++p)&lt;br /&gt;
            if (x % p.object == 0) {&lt;br /&gt;
                is_prime = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        if (is_prime) {&lt;br /&gt;
            primes.push(x); //помещаем число в список&lt;br /&gt;
            print(&amp;quot;the #{primes.size()} prime number is #{x}\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм поиска такой: берем число и начинаем вычислять остаток от его деления на все элементы нашего списка, то есть, выполняем попытку разложения числа на простые сомножители. Если число делится без остатка хотя бы на один из сомножителей (условие в строке 9), то число не является простым. Следовательно проверять его дальше не имеет смысла. Мы прекращаем цикл с помощью оператора &amp;lt;tt&amp;gt;'''break'''&amp;lt;/tt&amp;gt; и сбрасываем флаг ''is_prime''. Если после перебора всех множителей, флаг остался установленным, то это значит, что текущее число (переменная ''x'') является и вправду простым: его мы помещаем в конец списка. Так продолжается до тех пор, пока в списке не окажится 100 элементов. Для проверки размера списка (условие [[Основные синтаксические конструкции#Цикл while|цикла &amp;lt;tt&amp;gt;'''while'''&amp;lt;/tt&amp;gt;]] в строке 4) применяется метод &amp;lt;tt&amp;gt;size()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на то, каким образом реализуется перебор значений списка. Для этой операции применяется ''итератор''. Итератор — это специальный класс, объекты которого используются в качестве указателей на элементы контейнеров. Как правило, итераторы имеют методы для перемещения по контейнеру (операторы &amp;lt;tt&amp;gt;++&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;--&amp;lt;/tt&amp;gt;) и некоторое свойство, позволяющее обратиться к текущему элементу, на который указывает итератор. В случае класса &amp;lt;tt&amp;gt;list_iterator&amp;lt;/tt&amp;gt;, это свойство &amp;lt;tt&amp;gt;object&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для перебора списка применяется [[Основные синтаксические конструкции#Цикл for|цикл &amp;lt;tt&amp;gt;'''for'''&amp;lt;/tt&amp;gt;]]. В инициализаторе цикла была создана управляющая переменная ''p'', которая и является инстанцией итератора (класса &amp;lt;tt&amp;gt;list_iterator&amp;lt;/tt&amp;gt;). Список имеет специальный метод &amp;lt;tt&amp;gt;begin()&amp;lt;/tt&amp;gt;, который создает инстанцию итератора и устанавливает ее на свое начало. &lt;br /&gt;
&lt;br /&gt;
Метод &amp;lt;tt&amp;gt;end()&amp;lt;/tt&amp;gt; возвращает значение специального итератора, который всегда указывает на конец списка. Его нельзя двигать, и служит он только для одной цели — для проверки граничных условий. Важно понимать, что под концом списка понимается не последний его элемент, а именно конец — нечто, следующее за конечным элементом. Таким образом, условие в цикле &amp;lt;tt&amp;gt;'''for'''&amp;lt;/tt&amp;gt; проверяет, есть ли еще элементы &amp;quot;справа&amp;quot; от итератора ''p''. Если условие истинно, значит итератор находится где то в середине списка и можно продолжать итерации; если ложно — значит предыдущий элемент был последним и больше элементов в списке нет.&lt;br /&gt;
&lt;br /&gt;
Вот второй способ реализации того же участка кода, но как уже говорилось выше, с помощью массива:&lt;br /&gt;
&amp;lt;source lang=kpp line=1&amp;gt;&lt;br /&gt;
function GetPrimes() {&lt;br /&gt;
    var primes = new array;&lt;br /&gt;
    primes.resize(100); //задаем размер массива&lt;br /&gt;
    var x = 1;&lt;br /&gt;
    var count = 0; //текущее число найденных простых&lt;br /&gt;
    while (count &amp;lt; 100) {&lt;br /&gt;
        x++; //текущий кандидат&lt;br /&gt;
        var is_prime = true;&lt;br /&gt;
        for (var i = 0; i &amp;lt; count; ++i)&lt;br /&gt;
            if (x % primes[i] == 0) {&lt;br /&gt;
                is_prime = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        if (is_prime) {&lt;br /&gt;
            primes[count++] = x; //помещаем число в массив&lt;br /&gt;
            print(&amp;quot;the #{count} prime number is #{x}\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку мы заранее знаем размер массива, который нам может потребоваться (100 элементов), мы можем выделить всю необходимую память одним махом. Это будет намного быстрее, чем выделять память по мере итераций, ведь копирование элементов массива в новое место будет произведено всего один раз. Для изменения размера массива применяется метод &amp;lt;tt&amp;gt;resize()&amp;lt;/tt&amp;gt;. В качестве аргумента метод принимает число — размер массива, который мы желаем получить. Если желаемый размер меньше текущего, то массив будет урезан до нового размера (элементы, оказавшиеся &amp;quot;за бортом&amp;quot;, будут выброшены). Если больше — массив будет расширен, причем все старые элементы останутся невредимыми на своих местах, а новые примут значение &amp;lt;tt&amp;gt;'''null'''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввиду того, что размер массива был задан заранее, проверять конечное условие по количеству элементов нельзя (оно уже равно 100). Поэтому, была введена новая переменная ''count'', в которой мы будем хранить текущее количество найденных простых чисел, а поскольку размещаем мы их строго последовательно, то и индекс последнего найденного простого числа всегда будет известен (какой?).&lt;br /&gt;
&lt;br /&gt;
При работе с массивами никакие итераторы нам не нужны — у нас есть индексы. Для доступа к некоторому элементу массива нам надо знать его индекс. Сама операция получения значения элемента, осуществляется через оператор индексного доступа &amp;lt;tt&amp;gt;[]&amp;lt;/tt&amp;gt;. То есть, в нашем случае, &amp;lt;tt&amp;gt;primes[0]&amp;lt;/tt&amp;gt; будет соответствовать первому элементу массива, &amp;lt;tt&amp;gt;primes[1]&amp;lt;/tt&amp;gt; — второму, и т. д. (не забывайте, что индексация производится с нуля).&lt;br /&gt;
&lt;br /&gt;
Сам алгоритм поиска чисел, практически один-в-один повторяет алгоритм, описанный в предыдущем примере, так что подробно мы его описывать не будем. В качестве упраждения на внимательность, вы можете попытаться найти все отличия. &lt;br /&gt;
&lt;br /&gt;
Давайте лучше попробуем проанализировать оба решения и выяснить, какое из решений более оптимально для поставленной задачи. Для того, чтобы ответить на этот вопрос, необходимо прикинуть общее количество операций добавления элементов в массив и сравнить с количеством операций доступа к массиву, или операций выборки. &lt;br /&gt;
&lt;br /&gt;
В нашем алгоритме, мы видим следующее: на каждое число-кандидат, мы вынуждены пробегать все элементы коллекции и выполнять над ними некоторые действия. Поскольку, выборка элементов происходит строго последовательно, особой разницы между массивом и списком тут нет. Операция добавления элементов также происходит строго в конец коллекции и довольно редко. В целом, учитывая произведенные оптимизации (вроде предустановки размера массива), можно заключить что сложности алгоритмов примерно равны. &lt;br /&gt;
&lt;br /&gt;
Не забывайте, что это связано с жестко заданными условиями работы алгоритма! Представьте, если бы нам требовалось реализовать алгоритм, выбирающий элементы из коллекции не последовательно, а некоторым случайным, или почти случайным образом. В случае списка, итераторы нам уже не помогли бы. Пришлось бы опять прибегать к операции последовательного прохождения цепочки для отыскания нужного элемента. В таких условиях, по скорости массив был бы далеко впереди списка. Чуть изменим условия — и все может перевернуться вверх дном. Предположим, что реализуется алгоритм, при котором элементы добавляются не в конец, а в середину коллекции, да еще заранее не известно, какой может быть размер коллекции (так что невыгодно применять &amp;lt;tt&amp;gt;resize()&amp;lt;/tt&amp;gt;). В таких условиях, уже список будет выигрывать у массива.&lt;br /&gt;
&lt;br /&gt;
Получается, что от используемого алгоритма (а так же от четкости поставленных условий), во многом зависит то, насколько эффективен будет тот или иной контейнер. Вот почему при проектировании программ, требуется тщательным образом подходить к вопросу анализа вычислительной сложности алгоритмов и выбирать более подходящие средства для их реализации. Но как всегда, идеальных решений не бывает — выбор будет лежать где то посередине между ресурсоемкостью и производительностью.&lt;br /&gt;
&lt;br /&gt;
== Хеши ==&lt;br /&gt;
&lt;br /&gt;
Хеши являются еще одной формой контейнеров. Подобно массивам и спискам, они могут хранить в себе другие объекты, однако они отличаются и от тех и от других. Для доступа к элементу списка, необходимо использовать итераторы, в то время как для ссылки на элемент массива применяются индексы — числа, которые соответствуют номеру элемента в массиве. &lt;br /&gt;
&lt;br /&gt;
В этом отношении, хеши похожи на массивы, однако, для доступа к элементу используются не индексы, а совершенно произвольные объекты; или если перефразировать наоборот, то: для доступа к некоторому элементу хеша применяются индексы, которые могут быть представлены любым объектом: числом, строкой либо экземпляром пользовательского класса.&lt;br /&gt;
&lt;br /&gt;
Хеши обычно применяются там, где требуется организовать связь нескольких объектов. При этом, первому объекту-индексу, который в хешах называется ''ключом'', ставится в соответствие другой объект — ''значение''. Хеши хранят множество таких пар ключ-значение и, подобно спискам и массивам, предоставляют удобные методы для работы со своим содержимым.&lt;br /&gt;
&lt;br /&gt;
Чаще всего, хеши применяются для связи текстовых строк или для организации ''ассоциативных массивов'', у которых в качестве индекса применяется строка. Приведем несколько примеров применения хешей. Допустим, мы хотим поставить в соответствие названию дня недели его порядковый номер. То есть, строке &amp;quot;понедельник&amp;quot; должно соответствовать число 1, &amp;quot;вторнику&amp;quot; — 2 и так далее. Конечно, эту задачу можно решить и традиционным способом, например с помощью массивов:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
const weekdays = ['понедельник', 'вторник', 'среда', 'четверг',&lt;br /&gt;
                  'пятница', 'суббота', 'воскресенье'];&lt;br /&gt;
&lt;br /&gt;
function int WeekDay2DayNr(const string weekday) {&lt;br /&gt;
    var result; //результат (динамическая переменная)&lt;br /&gt;
    weekdays.each_pair() { |nr, day| &lt;br /&gt;
        if (day == weekday) { &lt;br /&gt;
            result = nr + 1; &lt;br /&gt;
            break; &lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
    if (result) //если переменной присвоено значение&lt;br /&gt;
        return result; //возвращаем его&lt;br /&gt;
    else //иначе, сообщаем об ошибке&lt;br /&gt;
        throw EInvalidCall.create('указанная строка не является днем недели');&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом примере, мы заводим массив ''weekdays'', в который помещаем названия всех дней недели. &lt;br /&gt;
&lt;br /&gt;
В функции &amp;lt;tt&amp;gt;WeekDay2DayNr()&amp;lt;/tt&amp;gt; мы перебираем все элементы массива и сравниваем их с контрольной строкой. Если происходит совпадение, то мы сохраняем текущее значение индекса (''nr'') в динамическую переменную ''result'' и прерываем цикл с помощью оператора &amp;lt;tt&amp;gt;'''break'''&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Далее, с помощью условного оператора проверяется, было ли переменной ''result'' присвоено некоторое значение. Если было, то оно возвращается как результат функции; если нет (то есть, оно по прежнему равно &amp;lt;tt&amp;gt;'''null'''&amp;lt;/tt&amp;gt;) — генерируется [[Идеология языка#Понятие исключения|исключение]].&lt;br /&gt;
&lt;br /&gt;
Данная функция будет исправно работать, однако в реальных условиях, при большом количестве элементов массива, операция поиска перебором может стать очень медленной. В таком случае, наилучшим решением, будет использование хешей. Перепишем предыдущую функцию так, чтобы она использовала хеш вместо массива: &lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
const weekdays = {'понедельник' =&amp;gt; 1, 'вторник' =&amp;gt; 2, 'среда'   =&amp;gt; 3, &lt;br /&gt;
                  'четверг'     =&amp;gt; 4, 'пятница' =&amp;gt; 5, 'суббота' =&amp;gt; 6, &lt;br /&gt;
                  'воскресенье' =&amp;gt; 7};&lt;br /&gt;
&lt;br /&gt;
function int WeekDay2DayNr(const string day) {&lt;br /&gt;
    try {&lt;br /&gt;
        return weekdays[day];&lt;br /&gt;
    } catch (ERangeError e) {&lt;br /&gt;
        throw EInvalidCall.create('указанная строка не является днем недели');&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Посмотрите, насколько проще стал код. А главное, он стал намного быстрее! Хеши позволяют значительно повысить скорость доступа к информации за счет того, что вместо обычного перебора, применяются альтернативные методы. Если говорить кратко, то происходит операция &amp;quot;перемешивания&amp;quot; ключа, которая осуществляется с помощью [http://ru.wikipedia.org/wiki/Хеш-функция хеш-функции], в результате которой получается некоторое значение — хеш ключа. Это значение уже используется для выборки искомого объекта. Скорость операции возрастает за счет того, что от медленной операции сравнения ключей, мы переходим к быстрой операции сравнения хешей. Хеши изначально проектируются так, чтобы их было легко сравнивать; в то же время, они должны быть различными для разных значний ключа. Более подробно, про хеши и хеширование, можно [http://ru.wikipedia.org/wiki/Хеш-таблица почитать на Википедии].&lt;br /&gt;
&lt;br /&gt;
Для объявления хеш-таблицы ''weekdays'' применяется специальный синтаксис, встроенный в язык К++: необходимо в фигурных скобрах перечислить пары ключ-значение, отделяя их запятой. Для связи ключа и значения в паре, применяется оператор соответствия (&amp;lt;tt&amp;gt;=&amp;gt;&amp;lt;/tt&amp;gt;). Подобно массивам, объекты хеш-таблиц могут объявляться где угодно в коде, будь то инициализатор переменной, либо фактический параметр в коде вызова функции. В нашем случае, объект создается в инициализаторе [[константы]], которая будет иметь тип &amp;lt;tt&amp;gt;hash&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Разумеется, для заполнения хеша можно применять и обычную операцию создания объекта, с последующим вызовом методов &amp;lt;tt&amp;gt;insert()&amp;lt;/tt&amp;gt; для добавления информации в таблицу:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var my_config = new hash;&lt;br /&gt;
my_config.insert('verbosity', 3);&lt;br /&gt;
my_config.insert('user name', 'nobody');&lt;br /&gt;
my_config.insert('password', 'y&amp;amp;5#3Eff_');&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Для заполнения хеша можно применять так же оператор индексного доступа:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
my_config[:path] = 'diss:/etc/myconf';&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Между вызовом метода &amp;lt;tt&amp;gt;insert()&amp;lt;/tt&amp;gt; и использованием оператора &amp;lt;tt&amp;gt;[]=&amp;lt;/tt&amp;gt; есть небольшая разница. Она заключается в том, что случае, если в хеше уже присутствует пара значений с тем же ключом, то метод &amp;lt;tt&amp;gt;insert()&amp;lt;/tt&amp;gt; сгенерирует [[Идеология языка#Понятие исключения|исключение]], в то время как оператор &amp;lt;tt&amp;gt;[]=&amp;lt;/tt&amp;gt; заменит старое значение на новое. Существует так же метод &amp;lt;tt&amp;gt;replace()&amp;lt;/tt&amp;gt;, который ведет себя подобно вышеозначенному оператору:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
my_config[:enabled] = 'true';&lt;br /&gt;
my_config.replace('enabled', 'true');&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Хеши, подобно массивам и спискам, обладают набором методов для перебора их содержимого, однако смысл некоторых методов немного отличается от своих аналогов в массивах:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
weekdays.each() { |day, nr| print(&amp;quot;key: #{day}, value: #{nr}\n&amp;quot;); };&lt;br /&gt;
var keys = weekdays.keys();&lt;br /&gt;
keys.each() { |key| println(key); };&lt;br /&gt;
weekdays.values().each() { |value| print(value as string + ' '); };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом примере используются три различных метода, которые вызывают блок с парой параметров ключ-значение (метод &amp;lt;tt&amp;gt;each()&amp;lt;/tt&amp;gt;), либо возвращают массивы ключей и значений (методы &amp;lt;tt&amp;gt;keys()&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;values()&amp;lt;/tt&amp;gt; соответственно). Во второй и третьей строках, показывается способ отображения всех ключей хеша, с помощью метода &amp;lt;tt&amp;gt;keys()&amp;lt;/tt&amp;gt; и промежуточной переменной. В последней строке вызовы методов совмещены в одной конструкции. Для наглядности приведем вывод, каким он должен был бы быть, при выполнении этого кода (подразумевается использование хеша ''weekdays'' из предыдущих примеров):&lt;br /&gt;
&lt;br /&gt;
 key: понедельник, value: 1&lt;br /&gt;
 key: вторник, value: 2&lt;br /&gt;
 key: среда, value: 3&lt;br /&gt;
 key: четверг, value: 4&lt;br /&gt;
 key: пятница, value: 5&lt;br /&gt;
 key: суббота, value: 6&lt;br /&gt;
 key: воскресенье, value: 7&lt;br /&gt;
 понедельник&lt;br /&gt;
 вторник&lt;br /&gt;
 среда&lt;br /&gt;
 четверг&lt;br /&gt;
 пятница&lt;br /&gt;
 суббота&lt;br /&gt;
 воскресенье&lt;br /&gt;
 1 2 3 4 5 6 7&lt;br /&gt;
&lt;br /&gt;
'''Примечание 1:''' Здесь мы перечислили выводимые значения по порядку. Однако, на практике такого не случается. За счет операции перемешивания, порядок следования элементов теряется. Поэтому, если требуется сохранить порядок, необходимо либо где-то записывать очредность ключей, либо сортировать их при обработке. Например, код обработки элементов хеша в порядке очередности ключей может выглядеть примерно так:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
my_hash.keys().sort().each() { |key| print(&amp;quot;key: #{key}, value: #{my_hash[key]}\n&amp;quot;); };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Примечание 2:''' Как уже было сказано выше, хеши позволяют использовать объекты любого типа как в качестве ключа, так и в качестве значения. Для того, чтобы можно было указывать в качестве ключа объект пользовательского типа, он должен иметь реализацию метода &amp;lt;tt&amp;gt;hash()&amp;lt;/tt&amp;gt;, не принимающего параметров и возвращающего объект класса &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;. Дополнительно, класс объектов ключа должен предоставлять операторы сравнения и присвоения (&amp;lt;tt&amp;gt;==&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;=&amp;lt;/tt&amp;gt;). На объекты, содержащиеся в значениях, никаких ограничений не накладывается. &lt;br /&gt;
&lt;br /&gt;
Таким образом, реализация класса ключей должена выглядеть примерно так:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
class MyClass {&lt;br /&gt;
public:&lt;br /&gt;
    export const function int hash() {&lt;br /&gt;
        /* здесь идет код вычисления хеша */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const operator bool == (const MyClass x) {&lt;br /&gt;
        /* код сравнения инстанций */ &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    operator MyClass = (const MyClass src) {&lt;br /&gt;
        /* код присвоения */ &lt;br /&gt;
        return this;&lt;br /&gt;
    }&lt;br /&gt;
    /* другие методы */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Указатели ==&lt;/div&gt;</summary>
		<author><name>Kamillen</name></author>	</entry>

	<entry>
		<id>http://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</id>
		<title>Стандартные типы данных</title>
		<link rel="alternate" type="text/html" href="http://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"/>
				<updated>2009-10-28T20:22:03Z</updated>
		
		<summary type="html">&lt;p&gt;Kamillen: /* Хеши */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;В этой главе будут рассмотрены базовые типы данных, применяющиеся в языке К++. Большинство из них объявлены в стандартной библиотеке Gide, однако некоторые, например интервалы, описываются в системной библиотеке самого языка К++. Еще раз напомним Читателю, что в языке К++, стандартные типы данных не являются встроенными. Конечно, компилятор опирается на них при генерации кода, но это совершенно не означает, что их нужно воспринимать как что-то единожды определенное и неизменное. Как уже было показано в книге, как с точки зрения компилятора так и самой виртуальной машины, эти классы  ничем не отличаются от обычных, пользовательских классов, когда дело касается их использования на высоком уровне. Если забежать вперед, то можно отметить, что на низком уровне они реализованы на языке C++ (из соображений производительности) и их интерфейсы представлены в стандартной библиотеке. Тем не менее, существует возможность их дополнения программистом-пользователем, что и было проделано в главах, посвященных [[Классы и объекты#Расширения|расширениям]]. Разумеется, от этих классов возможно наследовать собственные классы, точно так же, как и от любых других. В этой главе мы рассмотрим стандартные типы данных с точки зрения их применения и укажем некоторые свойства которые не были упомянуты в ходе повествования.&lt;br /&gt;
&lt;br /&gt;
== Целые числа ==&lt;br /&gt;
&lt;br /&gt;
Основой всей арифметики являются числа. Для представления чисел в программах применяются классы, объекты которых выступают как хранилища значений. В стандартной библиотеке, это класс &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;, умеющий хранить числа в диапазоне от -9223372036854775808 до 9223372036854775807 (64 битное целое со знаком).&lt;br /&gt;
&lt;br /&gt;
Класс поддерживает все необходимые арифметические операции, а так же имеет операторы преобразования в строку и из строки. Однако, следует быть осторожным и не полагаться полностью на автоматическое приведение типов. В некоторых случаях, можно получить не те результаты, что ожидает программист. Например:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var i = 1;&lt;br /&gt;
var j = &amp;quot;2&amp;quot;;&lt;br /&gt;
puts(i + j);&lt;br /&gt;
puts(j + i);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В приведенном выше примере, объявлены две переменных: целочисленная ''i'' и переменная ''j'', содержащая строку &amp;quot;2&amp;quot;. При вычислении первой суммы происходит следующее: компилятор &amp;quot;видит&amp;quot;, что необходимо посчитать сумму переменных ''i'' и ''j'', которые для нас являются представлениями чисел 1 и 2. Но с точки зрения компилятора, это всего лишь две инстанции классов &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
Разбор выражения производится слева-направо, так же как его считает человек. Компилятор пытается найти в классе &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; оператор &amp;lt;tt&amp;gt;+&amp;lt;/tt&amp;gt; с типом второго операнда, то есть &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;. Такого оператора в классе нет. Тогда компилятор смотрит, существует ли в классе &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt; [[Типы операторов#Операторы приведения типов|оператор приведения]] к типу &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;, либо к другому типу, для которого в классе &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; объявлен оператор &amp;lt;tt&amp;gt;+&amp;lt;/tt&amp;gt;. Такой оператор находится, соответственно компилятор генерирует вызов оператора приведения для переменной ''j'', а затем уже выполняется сам оператор &amp;lt;tt&amp;gt;+&amp;lt;/tt&amp;gt;. Поэтому, сложение происходит численно, так как и ожидалось. Результат суммы будет соответствовать числу 3:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
i + j = 1 + (&amp;quot;2&amp;quot; as int) = 1 + 2 = 3&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Во втором случае, все происходит с точностью наоборот: первым операндом является строка, то есть инстанция класса &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;. В этом классе так же объявлен оператор &amp;lt;tt&amp;gt;+&amp;lt;/tt&amp;gt;, но принимающий  в качестве второго операнда строку. Этот оператор используется для конкатенации двух строк, при которой вторая строка дописывается в конец первой, то есть:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s1 = &amp;quot;hello&amp;quot;;&lt;br /&gt;
var s2 = &amp;quot;world&amp;quot;;&lt;br /&gt;
puts(s1 + s2); //результат: &amp;quot;hello&amp;quot; + &amp;quot;world&amp;quot; = &amp;quot;helloworld&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В нашей сумме, вторым операндом является инстанция класса &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;, которая по вышеописанным правилам, будет приведена к типу &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;. Таким образом, операция суммы будет приведена к операции конкатенации строк &amp;quot;2&amp;quot; и &amp;quot;1&amp;quot;, то есть результат выражения будет &amp;quot;21&amp;quot;:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
j + i = &amp;quot;2&amp;quot; + (1 as string) = &amp;quot;2&amp;quot; + &amp;quot;1&amp;quot; = &amp;quot;21&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Приведенная проблема особенно опасна при использовании [[Переменные#Нетипированные (динамические) переменные|динамических переменных]], поскольку в одном случае может произойти одна операция, а в другом другая. Поэтому, использовать динамические переменные следует очень осторожно. Если у вас нет уверенности относительно типа передаваемой переменной, лучше подстраховаться и произвести операцию явного преобразования к интересующему вас типу.&lt;br /&gt;
&lt;br /&gt;
== Числа с плавающей точкой ==&lt;br /&gt;
&lt;br /&gt;
Для проведения сложных математических рассчетов, одних целых чисел недостаточно. Для представления действительных чисел, или чисел с плавающей точкой (запятой), как их называют в информатике, в стандартной библиотеке создан класс &amp;lt;tt&amp;gt;real&amp;lt;/tt&amp;gt;, который на низком уровне представлен типом двойной точности (double). &lt;br /&gt;
&lt;br /&gt;
В отличие от класса &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;, класс &amp;lt;tt&amp;gt;real&amp;lt;/tt&amp;gt; корректно обрабатывается в сочетании с целочисленными классами. Например, следующие выражения дадут одинаковые результаты:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var i = 3 + 0.1415&lt;br /&gt;
var j = 0.1415 + 3&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате, обе переменных будут иметь тип &amp;lt;tt&amp;gt;real&amp;lt;/tt&amp;gt; и значение &amp;lt;!--примерно --&amp;gt;3,1415. Это достигается тем, что в классе &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; есть специальные версии арифметических операторов, которые принимают в качестве параметра объекты класса &amp;lt;tt&amp;gt;real&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Логический тип ==&lt;br /&gt;
&lt;br /&gt;
В некоторых случаях в программе бывает необходимо сохранять логическое значение. Это может потребоваться в алгоритмах, где такое значение используется в качестве флага, либо в свойствах объектов, которые подразумевают наличие или отсутствие некоторого признака. Это осуществляется с помощью булевых переменных и псевдотипа &amp;lt;tt&amp;gt;bool&amp;lt;/tt&amp;gt;. Переменные булевого типа могут иметь только два значения: &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;, соответствующее истине и &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;, соответствующее лжи.&lt;br /&gt;
&lt;br /&gt;
К переменным булевого типа могут приводиться значения более сложных выражений, например когда такое выражение находится в условии цикла. Правила преобразования значений выражений были описаны при рассмотрении [[Основные синтаксические конструкции#Оператор if|условного оператора &amp;lt;tt&amp;gt;'''if'''&amp;lt;/tt&amp;gt;]].&lt;br /&gt;
&lt;br /&gt;
'''Примечание:''' псевдотипом &amp;lt;tt&amp;gt;bool&amp;lt;/tt&amp;gt; назван потому, что он не является классом в полной мере.  Это абстракция, которая была введена в язык К++ для преодоления некоторых сложностей при проектировании языка. Переменные такого класса обрабатываются на уровне самого К++; с точки зрения Gide такого класса не существует (вернее, существуют специальные объекты &amp;lt;tt&amp;gt;'''@true'''&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;'''@false'''&amp;lt;/tt&amp;gt;, но это тема, требующая отдельного обсуждения).&lt;br /&gt;
&lt;br /&gt;
Существует несколько ограничений, которые важно понимать. Первое ограничение заключается в том, что от этого типа нельзя наследовать собственные классы. Второе ограничение проявляется в том, что переменные типа &amp;lt;tt&amp;gt;bool&amp;lt;/tt&amp;gt; обрабрабатываются особым образом. Если в выражении присутствует переменная булевого типа, то операторы отношения работают немного по другому. Например, следующее выражение будет трактовано как истина:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
if (0 == true)&lt;br /&gt;
   //...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Условие сработает потому что 0 представляет собой действительный, существующий объект, стало быть он истинен (об этом уже было написано). Вот другой пример, иллюстрирующий ту же проблему:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var MyClass c = new MyClass;&lt;br /&gt;
if (c == true)&lt;br /&gt;
   //...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подобное условие так же сработает (будет считаться истинным), поскольку оператор &amp;lt;tt&amp;gt;==&amp;lt;/tt&amp;gt; в данном случае обрабаытвается самим компилятором. Конечно, приведенные примеры являются довольно странными с точки зрения обычного кода, но их все же следует иметь в виду при написании программ.&lt;br /&gt;
&lt;br /&gt;
== Строки ==&lt;br /&gt;
&lt;br /&gt;
Для хранения и обработки строковых данных используется класс &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;. Он объявлен в [[Стандартная библиотека Gide|стандартной библиотеке Gide]] и дополнен с помощью [[Классы и объекты#Расширения|расширений]] в системной библиотеке К++. &lt;br /&gt;
&lt;br /&gt;
В языке К++ предусмотрены целых три синтаксиса для объявления строковых констант, которые в конечном счете представляются одним и тем же классом &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Строки, заключенные в двойные кавычки обрабатывают все escape последовательности, а так же позволяют использовать т. н. вставки, или подстановки — кусочки кода, которые вставляются непосредственно в тело строки и при работе программы заменяются на значение, возвращаемое этим кодом (см. ниже). Обрабатываются следующие последовательности:&lt;br /&gt;
::{|&lt;br /&gt;
 ! Последовательность  || Значение&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''\n'''&amp;lt;/tt&amp;gt;   || перенос строки&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''\r'''&amp;lt;/tt&amp;gt;   || возврат каретки&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''\t'''&amp;lt;/tt&amp;gt;   || символ табуляции&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''\&amp;quot;'''&amp;lt;/tt&amp;gt;   || символ двойной кавычки&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''\\'''&amp;lt;/tt&amp;gt;   || символ обратного слеша&lt;br /&gt;
 |-&lt;br /&gt;
 | &amp;lt;tt&amp;gt;'''#{ }'''&amp;lt;/tt&amp;gt; || подстановка выражения&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
При этом, при обработке строки, найденные последовательности заменяются на соответствующий им управляющий символ. Например, последовательность &amp;lt;tt&amp;gt;'''\n'''&amp;lt;/tt&amp;gt; будет заменена на один символ перевода строки (ASCII код 13).&lt;br /&gt;
&lt;br /&gt;
Подстановки это очень удобный способ совмещать в одном выражении строку которую необходимо вывести и выражения, значение которых необходимо добавить к выводу. Получается что подстановки это что-то среднее между простой строкой и строкой для форматируемого вывода. Подстановка начинается с пары символов &amp;lt;tt&amp;gt;'''#{'''&amp;lt;/tt&amp;gt;, которые должны идти друг за другом, без пробелов. То что идет дальше, воспринимается компилятором как выражение, как если бы оно было записано просто в коде, вне строки. Завершает подстановку соответствующая ей закрывающая фигурная скобка.&lt;br /&gt;
&lt;br /&gt;
Приведем пример использования подстановок:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s1 = &amp;quot;сумма чисел 2 и 3 равна #{2 + 3}.&amp;quot;;&lt;br /&gt;
print(&amp;quot;s1 = #{s1}\n&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При разборе выражения компилятор преобразовывает данное выражение в сумму, представляющую из себя последовательность строковых констант и выражений:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s1 = &amp;quot;сумма чисел 2 и 3 равна &amp;quot; + (2 + 3) + &amp;quot;.&amp;quot;;&lt;br /&gt;
print(&amp;quot;s1 = &amp;quot; + (s1) + &amp;quot;\n&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате выполнения этого кода, в выводе появится текст (курсор будет переведен на следующую строку):&lt;br /&gt;
 s1 = сумма чисел 2 и 3 равна 5.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Подстановки даже можно делать вложенными, например так:&lt;br /&gt;
&amp;lt;!--&amp;lt;source lang=kpp&amp;gt;--&amp;gt;&lt;br /&gt;
 var x = [ 1, 2, 3, 4, 5 ];&lt;br /&gt;
 var s = &amp;quot;some weird stuff: #{ x.join(', ') { |idx,elem| &amp;quot;#{idx} = #{elem}&amp;quot;; } }\n&amp;quot;;&lt;br /&gt;
&amp;lt;!--&amp;lt;/source&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если требуется работать со строкой, которая содержит escape последовательности (или подстановки), которые не требуется обрабатывать то следует применять строки, заключенные в одинарные кавычки:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s1 = 'в таких строках \n \r \t и #{подстановки} не обрабатываются';&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Исключением из правила являются последовательности '''&amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;\'&amp;lt;/nowiki&amp;gt;&amp;lt;/tt&amp;gt;''' и &amp;lt;tt&amp;gt;'''\\'''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Третьим способом объявить строку является применение синтаксиса кратких, &amp;quot;однословных&amp;quot; строк. Такие строки начинаются со знака двоеточия, следом за которым могут идти те же символы, что возможны при написании идентификаторов, то есть: латинские буквы, цифры и знак подчеркивания, притом что первой после двоеточия обязательно идет либо буква, либо подчеркивание. Такие строки могут быть особенно полезны при записи ключей хеша (как будет показано ниже), либо просто в тех местах где в качестве строки передается одно слово:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var h = { :sunday = 1, :monday = 2, :tuesday = 3 };&lt;br /&gt;
var x = my_function(:hello);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Так же, класс &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt; предоставляет пользователю широкий спектр различных методов, которые могут использоваться для работы со строками.&lt;br /&gt;
&lt;br /&gt;
=== Длина строки ===&lt;br /&gt;
&lt;br /&gt;
Для определения длины строки используется метод &amp;lt;tt&amp;gt;length()&amp;lt;/tt&amp;gt;, который возвращает число символов в строке. Так же, существует метод &amp;lt;tt&amp;gt;empty()&amp;lt;/tt&amp;gt; который возвращает истину если строка пуста и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
=== Выделение подстрок ===&lt;br /&gt;
&lt;br /&gt;
Для выделения из исходной строки подстрок, разделенных символом или несколькими символвами разделителями, используется метод &amp;lt;tt&amp;gt;split()&amp;lt;/tt&amp;gt;. В качестве параметра, этот метод принимает строку, либо [[Введение, или краткий обзор#Регулярные выражения|регулярное выражение]], соответствующие разделителю. Возвращает метод уже массив из полученных подстрок.&lt;br /&gt;
&lt;br /&gt;
В качестве примера приведем два способа обработки URL строки и выделения из нее отдельных частей:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var url = &amp;quot;http://example.com?key1=value1&amp;amp;key2=value2&amp;amp;key3=value3&amp;quot;;&lt;br /&gt;
var qm = url.find(&amp;quot;?&amp;quot;);&lt;br /&gt;
var args = url.substr(qm, url.length());&lt;br /&gt;
var pairs = args.split('&amp;amp;');&lt;br /&gt;
var options = new hash;&lt;br /&gt;
pairs.each() { |pair| &lt;br /&gt;
    var kv = pair.split('='); &lt;br /&gt;
    options[kv[0]] = kv[1]; &lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С помощью метода &amp;lt;tt&amp;gt;find()&amp;lt;/tt&amp;gt; мы находим позицию символа вопроса в исходной строке, который разделяет адрес на собственно адрес ресурса и на строку параметров запроса. Строка параметров состоит из множества пар ''значение''&amp;lt;tt&amp;gt;=&amp;lt;/tt&amp;gt;''результат'', отделенных друг от друга символом амперсанда. Выделив подстроку запроса, мы разделяем ее на подстроки с помощью метода &amp;lt;tt&amp;gt;split()&amp;lt;/tt&amp;gt;, указывая амперсанд в качестве разделителя. Полученный массив подстрок мы сохраняем в переменной ''pairs''. Теперь, мы заводим [[Стандартные типы данных#Хеши|хеш]] ''options'' в который будем записывать параметры запроса, по мере его обработки. Выполняя перебор по всем парам, мы опять разделяем их на две части, на этот раз по символу равенства. Последним действием, полученная пара значение-результат сохраняется в хеше. &lt;br /&gt;
&lt;br /&gt;
Несмотря на то, что вышеприведенный код работает, он является не очень эффективным. В процессе его работы создается много промежуточных переменных и может задействоваться большое количество памяти (при длинных строках запросов). При задачах обработки строк выгодно использовать регулярные выражения. Они позволяют относительно легко производить обработку строк; выполнять поиск и замену по некоторому шаблону. Приведем другой, более элегантный способ решения той же самой задачи, но уже с использованием регулярных выражений:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var options = new hash;&lt;br /&gt;
if (url =~ `\?(.*)$`) {&lt;br /&gt;
    //разделяем строку на пары значение=результат&lt;br /&gt;
    var pairs = $[1].value.split('&amp;amp;');&lt;br /&gt;
    //обрабатываем пары по очереди&lt;br /&gt;
    pairs.each() { |pair|&lt;br /&gt;
        if (pair =~ `^([\w\d]+)=([\w\d]+)$`)&lt;br /&gt;
            options[$[1].value] = $[2].value;&lt;br /&gt;
    };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оператор &amp;lt;tt&amp;gt;=~&amp;lt;/tt&amp;gt; служит для связи исходной строки с регулярным выражением. В результате, в специальный массив &amp;lt;tt&amp;gt;$[]&amp;lt;/tt&amp;gt; будут помещены все совпадения. Элемент с индексом 0 будет соответствовать всей совпавшей подстроке, в остальных элементах будут находиться совпадения, соответствующие подвыражениям в скобках. &lt;br /&gt;
&lt;br /&gt;
'''Примечание 1:''' элементы спецмассива &amp;lt;tt&amp;gt;$[]&amp;lt;/tt&amp;gt; представляют собой не сами подстроки, а объекты-совпадения, класса &amp;lt;tt&amp;gt;regexp_match&amp;lt;/tt&amp;gt;. В этих объектах содержится дополнительная информация о совпадении, например: &lt;br /&gt;
* индексы левой и правой границы (свойства &amp;lt;tt&amp;gt;left&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;right&amp;lt;/tt&amp;gt;)&lt;br /&gt;
* длина подстроки (свойство &amp;lt;tt&amp;gt;length&amp;lt;/tt&amp;gt;)&lt;br /&gt;
* и, естественно, сама подстрока (свойство &amp;lt;tt&amp;gt;value&amp;lt;/tt&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Получить доступ к подстроке так же можно, выполнив явное приведение объекта совпадения к типу &amp;lt;tt&amp;gt;string&amp;lt;/tt&amp;gt;. Таким образом, следующие строки функционально эквивалентны:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var substr1 = $[1].value;&lt;br /&gt;
var substr2 = $[1] as string;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Примечание 2:'''  в принципе, можно было и не объявлять отдельную переменную ''pairs''. В книге это было сделано для большей читаемости кода, однако на практике может применяться и более лаконичная форма, которая, тем не менее, остается довольно удобной к восприятию:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
if (url =~ `\?(.*)$`) {&lt;br /&gt;
    $[1].value.split('&amp;amp;').each() { |pair|&lt;br /&gt;
        if (pair =~ `^([\w\d]+)=([\w\d]+)$`)&lt;br /&gt;
            options[$[1].value] = $[2].value;&lt;br /&gt;
    };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При желании, можно еще больше упростить код, используя версию оператора &amp;lt;tt&amp;gt;[]&amp;lt;/tt&amp;gt;, принимающую регулярное выражение в качестве параметра и возвращающее соответствующую ему подстроку:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
url[`\?(.*)$`].value.split('&amp;amp;').each() { |pair|&lt;br /&gt;
    if (pair =~ `^([\w\d]+)=([\w\d]+)$`)&lt;br /&gt;
        options[$[1].value] = $[2].value;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако, в этом случае, при отсутствии требуемой подстроки в исходной переменной ''url'', будет сгенерировано исключение и это следует корректно обрабатывать.&lt;br /&gt;
&lt;br /&gt;
=== Объединение строк (конкатенация) ===&lt;br /&gt;
&lt;br /&gt;
Конкатенация строк осуществляется с помощью обычных арифметических операторов (при этом, приоритеты операций остаются теми же). В зависимости от оператора, действие будет производиться над самим объектом или над его копией:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s1 = &amp;quot;hello&amp;quot;;&lt;br /&gt;
var s2 = &amp;quot;world&amp;quot;;&lt;br /&gt;
var s3 = s1 + ' ' + s2; // s3 = &amp;quot;hello world&amp;quot;, s1 и s2 те же&lt;br /&gt;
s1 = (s3 += &amp;quot;!&amp;quot;);       // s1 = s3 = &amp;quot;hello world!&amp;quot;&lt;br /&gt;
s2 *= 2;                // s2 = worldworld&lt;br /&gt;
s3 = s1 + '!' * 2;      // s3 = &amp;quot;hello world!!!&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на две последние строки. Оператор &amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;, выполняемый над строкой действительно &amp;quot;умножает&amp;quot; ее, повторяя заданное количество раз.&lt;br /&gt;
&lt;br /&gt;
=== Операторы индексного доступа ===&lt;br /&gt;
&lt;br /&gt;
Для доступа к отдельным символам строки может использоваться оператор индексного доступа &amp;lt;tt&amp;gt;[]&amp;lt;/tt&amp;gt;. При этом, строка представляется как [[Стандартные типы данных#Массивы и списки|массив]], где каждый элемент соответствует '''номеру символа''' в unicode:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var str = &amp;quot;hello world&amp;quot;;&lt;br /&gt;
var ord = str[0]; // ord = 104&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Можно совершать и обратную операцию, а именно присваивать по некоторому индексу числовое значение. Тогда в строке будет стоять символ, соответствующий указанному числу:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
str[0] = 72;     // str = &amp;quot;Hello world&amp;quot;&lt;br /&gt;
str[6] = 'W'[0]; // str = &amp;quot;Hello World&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Существует дополнительный синтаксис, специально предназначенный для получения номера некоторого символа. Он осуществляется записью символа диез (&amp;quot;решетка&amp;quot;), с последующим добавлением исследуемого символа. Таким образом, вторую строку из предыдущего примера можно записать так:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
str[6] = #W;     // str = &amp;quot;Hello World&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот синтаксис можно применять даже для управляющих символов вроде '''\n''' и даже для любого символа Unicode:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var n = #\n;     // n будет содержать число 13&lt;br /&gt;
var f = #Ф;      // код, соответствующий русской заглавной букве Ф&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же требуется получить подстроку исходной строки на основании индексов символов, нужно применять [[Стандартные типы данных#Интервалы|интервалы]]:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s2 = str[0 .. 4]; // s2 = &amp;quot;Hello&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Возможно так же использовать оператор индексного доступа для присвоения значения отдельным подстрокам. Это осуществляется с помощью оператора &amp;lt;tt&amp;gt;[]=&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var greeting = &amp;quot;Welcome to the real world, Neo.&amp;quot;;&lt;br /&gt;
greeting['real world'] = 'matrix'; // Welcome to the matrix, Neo.&lt;br /&gt;
greeting[15 .. 20] = 'Deeptown';   // Welcome to the Deeptown, Neo.&lt;br /&gt;
greeting[-1] = #!;                 // Welcome to the Deeptown, Neo!&lt;br /&gt;
greeting[`\sNeo.$`] = 'Leonid!';   // Welcome to the Deeptown, Leonid!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В примере выше, применены четыре способа индексного доступа для изменения строки, соответствующие четырем различным операторам (точнее, четырем версиям перегруженного оператора). В первом случае используется доступ по подстроке. При этом подстрока будет &amp;quot;вырезана&amp;quot; из строки, а на ее место будет вставлена присваиваемая строка. Таким образом, эта операция не зависит от размера искомой и заменяемой подстрок (если заменяемая строка длиннее, исходная строка будет &amp;quot;раздвинута&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Во втором случае осуществляется доступ по интервалу. Символы с индексами в диапазоне 15-20 соответствуют слову &amp;quot;matrix&amp;quot;. Замена опять же производится по правилу &amp;quot;вырезания&amp;quot; подстроки с последующим &amp;quot;склеиванием&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
В третьем случае, используются отрицательные индексы, для указания того, что отсчитывать символы нужно с конца строки. Так, -1 соответствует последнему символу, -2 второму с конца и т. д.&lt;br /&gt;
&lt;br /&gt;
Наконец, в последней замене применяется [[Введение, или краткий обзор#Регулярные выражения|регулярное выражение]], для поиска подстроки.&lt;br /&gt;
&lt;br /&gt;
'''Примечание:''' Методы замены через оператор &amp;lt;tt&amp;gt;[]=&amp;lt;/tt&amp;gt; на деле являются обертками к соответствующим реализациям метода &amp;lt;tt&amp;gt;replace_all()&amp;lt;/tt&amp;gt;. При этом, будут производиться замены всех включений подстроки в исходную строку (разумеется, кроме случаев ин тервала и одиночного индекса). Если вы хотите заменить только одно включение, — используйте методы &amp;lt;tt&amp;gt;replace()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Интервалы == &lt;br /&gt;
&lt;br /&gt;
В некоторых случаях бывает необходимо передать в качестве параметра не отдельное значение а диапазон. Для указания диапазона обычно достаточно указать только его границы. Это может осуществляться с помощью класса &amp;lt;tt&amp;gt;interval&amp;lt;/tt&amp;gt;. К++ предоставляет специальный синтаксис для указания интервалов с помощью оператора &amp;quot;&amp;lt;tt&amp;gt;..&amp;lt;/tt&amp;gt;&amp;quot;; как уже было показано выше, это может применяться для выборки подстрок, соответствующих заданному диапазону индексов, либо подмассивов по тому же принципу:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s = &amp;quot;abcdef&amp;quot;[0..2];      // &amp;quot;abc&amp;quot;&lt;br /&gt;
var a = [1, 2, 3, 4][1..-1]; // [2, 3, 4]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вообще, интервалы могут быть не только численными. В качестве объектов, формирующих интервал, могут выступать любые два объекта, имеющие одинаковые типы:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var left = MyClass.Create(5), right = MyClass.Create(10);&lt;br /&gt;
var i = interval.create(left, right);&lt;br /&gt;
var alpahbet = 'a' .. 'z';&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Интервалы могут использоваться как виртуальные массивы, то есть к ним можно обращаться как к массиву, запрашивая некоторый элемент по индексу. И он будет возвращен, как будто действительно хранится в массиве (на сама деле, он создается в момент обращения по некоторому известному закону):&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var numbers = 1 .. 100;&lt;br /&gt;
var alpahbet = 'a' .. 'z';&lt;br /&gt;
var x = numbers[50];  // x = 1 + (50-1) = 50.&lt;br /&gt;
var y = alphabet[10]; // y = (#a + (10-1)).char = 'k'&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Преимуществом такого подхода является то, что не нужно хранить весь массив в памяти. Зная закон изменения элементов, можно получить любой элемент, зная базу (одну из границ) и индекс элемента.&lt;br /&gt;
&lt;br /&gt;
Подобно массивам, интервалы так же обладают методом &amp;lt;tt&amp;gt;each()&amp;lt;/tt&amp;gt;, позволяющем проходить по всему массиву,  выполняя некоторый блок с параметром текущего элемента массива:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var s = 0; //здесь будет сумма&lt;br /&gt;
var numbers = 1 .. 100;&lt;br /&gt;
numbers.each() { |x| s += x; };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для выяснения принадлежности некоторого объекта к интервалу могут применяться метод &amp;lt;tt&amp;gt;contains()&amp;lt;/tt&amp;gt; или соответствующий ему оператор &amp;lt;tt&amp;gt;'''in'''&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var numbers = 1 .. 100;&lt;br /&gt;
var lowercase = 'a' .. 'z';&lt;br /&gt;
var x = numbers.contains(75) ? &amp;quot;yes&amp;quot; : &amp;quot;no&amp;quot;;    //x = &amp;quot;yes&amp;quot;&lt;br /&gt;
var y = 'X' in lowercase ? &amp;quot;yes&amp;quot; : &amp;quot;no&amp;quot;; //y = &amp;quot;no&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для создания интервалов на базе собственных классов необходимо перегрузить конструктор, а так же соответствующие методы, своими реализациями.&lt;br /&gt;
&lt;br /&gt;
== Массивы и списки ==&lt;br /&gt;
&lt;br /&gt;
Для хранения наборов объектов применяются массивы и списки. И те и другие являются контейнерами, то есть, их объекты способны хранить в себе другие объекты и обладают соответствующими методами для добавления в них элементов и их извлечения. Разница между массивом и списком заключается в том, что массив хранит элементы в одной цельной области памяти, в то время как список организует их в виде цепочки связанных друг с другом элементов:&lt;br /&gt;
&lt;br /&gt;
[[Изображение:Array_vs_list.png|center]]&lt;br /&gt;
&lt;br /&gt;
У каждого из этих контейнеров есть свои преимущества и недостатки. Постараемся рассмотреть основные операции, осуществляемые с контейнерами и выяснить, в каких случаях предпочтительнее использовать массив, а в каких список.&lt;br /&gt;
&lt;br /&gt;
=== Доступ к данным ===&lt;br /&gt;
&lt;br /&gt;
Массивы обладают высокой скоростью доступа к информации, поскольку все данные находятся в одном месте и структурированы. То есть, доступ есть сразу ко всем элементам, поэтому не приходится двигаться вдоль массива для поиска нужного элемента. Списки работают несколко по другому. Для того, чтобы обратиться к некоторому элементу списка, необходимо пройти всю цепочку от начала до требуемого элемента (см. рисунок выше). Поэтому, списки значительно уступают по скорости доступа массивам. В случаях, когда содержимое контейнера меняется относительно редко, но присутствует большое количество обращений к данным, следует применять массивы.&lt;br /&gt;
&lt;br /&gt;
=== Добавление элементов ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим операцию добавления элементов в список. Предположим, что нам надо добавить элемент N в список, между вторым и третьим его элементами. Для выполнения этой операции, нам необходимо всего лишь &amp;quot;разорвать&amp;quot; цепочку в нужном месте и изменить указатель у предыдущего элемента X&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; так, чтобы он указывал на новый элемент N. Для того, чтобы продолжить цепочку, элемент N должен указывать на следующий элемент списка, то есть на X&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt;:&lt;br /&gt;
[[Изображение:List insert.png|center]]&lt;br /&gt;
&lt;br /&gt;
Операция добавления к началу или к хвосту списка так же не составляет труда, и сводится все к той же работе с указателями. Удаление элементов производится обратным образом: элемент удаляется, а указатель предыдущего элемента меняется так, чтобы он указывал на последующий за удаляемым элемент.&lt;br /&gt;
&lt;br /&gt;
В случае массивов все усложняется. Как уже было сказано выше, массивы хранят элементы в монолитных областях памяти, следовательно, чтобы добавить или удалить элемент, приходится задействовать весь массив. Если места в текущей области памяти, занятой массивом недостаточно, чтобы вместить еще один элемент, то произойдет выделение нового блока памяти, с последующим копированием всех существующих элементов массисва в новый блок. &lt;br /&gt;
&lt;br /&gt;
Операция выделения производится &amp;quot;с запасом&amp;quot;, то есть, размер нового блока будет больше, чем размер занимаемый всеми элементами, причем зависимость тут экспоненциальная: чем больеше будет добавляться элементов в массив, тем больше будет размер выделяемого блока памяти. Это делается для того, чтобы минимизировать риск полного копирования. Тем не менее, такое может случиться (а при операции вставки — случается обязательно), что в итоге может привести к значительной задержке.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим тот же случай, что и в случае со списком, а именно, операцию вставки нового элемента, между вторым и третьим элементами массива. Для выполнения этой операции создается новый массив, с размером, достаточным для помещения всех предыдущих элементов, а так же нового элемента. Далее, происходит копирование всех элементов на соответствующие места:&lt;br /&gt;
[[Изображение:Array insert.png|center]]&lt;br /&gt;
&lt;br /&gt;
Это случай неудачного стечения обстоятельств, при котором происходит копирование большого количества информации, что естественным образом сказывается на призводительности всей операции. Из этого можно сделать вывод, что массивы следует применять тогда, когда операции вставки или добавления элементов происходят редко. В случаях, когда необходимо организовывать стеки или очереди, где обращение по номеру элемента не требуется, наилучшим решением будет использование списков.&lt;br /&gt;
&lt;br /&gt;
=== Применение ===&lt;br /&gt;
&lt;br /&gt;
Разобравшись с философией работы массивов и списков, поняв разницу между ними и определившись с областями применения тех и других, мы переходим непосредственно к практике. Массивы и списки в языке К++ представляются классами &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;list&amp;lt;/tt&amp;gt; соответственно. Оба класса имеют очень похожие наборы методов, так что дважды изучать одно и то же не придется.&lt;br /&gt;
&lt;br /&gt;
Объявление массива реализуется с помощью специального синтаксиса, встроенного в язык К++. Это может осуществляться как в инициализаторе переменной, так и в любом другом выражении, вплоть до фактического параметра в коде вызова функции, или даже для указания значения по умолчанию для него. &lt;br /&gt;
&lt;br /&gt;
Для того, чтобы объявить массив, необходимо перечислить его элементы, заключив их в квадратные скобки и отделив друг от друга запятой:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var my_array = [1, 2, 3];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В качестве элементов массива могут быть указаны любые объекты: числа, строки, экземпляры классов, блоки и даже вложенные массивы с хешами — массиву все равно что хранить, поскольку все что он делает, это хранит ссылки на объекты:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var myobject = new MyClass;&lt;br /&gt;
var object_dump = [1, 3.14, 'hello', myobject, {|x| return x + 1;}, &lt;br /&gt;
    {:a =&amp;gt; 1, :b =&amp;gt; 2 }, [1,2,3], true, 10 .. 20];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Можно сказать, что массив представляет собой реализацию абстрактного упорядоченного хранилища данных с возможностью произвольного доступа.&lt;br /&gt;
&lt;br /&gt;
Специального синтаксиса для создания списков нет, поскольку большинство задач которые решают списки не предполагают инициализации значениями. Если же это все таки потребовалось, на помощь придут массивы. Вот небольшой пример того как можно занести в список значения из массива, создаваемого компилятором автоматически:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var my_list = new list;&lt;br /&gt;
[1, 2, 3].each() { |x| my_list.push(x); };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом примере показываются сразу два важных приема, которые являются одними из самых частых по использованию (если вы внимательно читали книгу, то наверняка это заметили). Метод &amp;lt;tt&amp;gt;each()&amp;lt;/tt&amp;gt; используется для перебора всего содержимого коллекции (массива, списка или интервала) и вызова некоторого блока, передавая ему на каждой итерации значение текущего элемента. Таким образом, в случае нашего массива, блок будет вызыван три раза, с параметрами, соответственно, 1, 2 и 3. Метод &amp;lt;tt&amp;gt;push()&amp;lt;/tt&amp;gt; присутствует как в массивах, так и в списках, и служит для добавления элемента в конец коллекции. Здесь он применяется для добавления текущего элемента массива в список. Получается, что по выполнении этой процедуры, список будет содержать в себе все элементы, присутствующие в массиве (в том же порядке).&lt;br /&gt;
&lt;br /&gt;
Существуют еще несколько методов, похожих на метод &amp;lt;tt&amp;gt;each()&amp;lt;/tt&amp;gt;, но отличающихся передаваемыми в блок параметрами. Например &amp;lt;tt&amp;gt;each_index()&amp;lt;/tt&amp;gt; вызывает блок, передавая ему текущий индекс, а метод &amp;lt;tt&amp;gt;each_pair()&amp;lt;/tt&amp;gt; передает ему текущий индекс и элемент. За неимением индекса, списки два последних метода не реализуют. Переделаем немного предыдущий пример и покажем использование этих методов:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var my_array = [1, 2, 3];&lt;br /&gt;
var my_list = new list;&lt;br /&gt;
my_array.each() { |x| my_list.push(x); };&lt;br /&gt;
&lt;br /&gt;
my_list.each() { |x| print(&amp;quot;#{x}\n&amp;quot;); };&lt;br /&gt;
my_array.each_pair() { |i, e| print(&amp;quot;my_array[#{i}] = #{e}\n&amp;quot;); };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Иногда бывает необходимо поместить элементы массива или списка в строку. Например, чтобы вывести их пользователю или, возможно, записать в файл некоторые настройки, подразумевающие список значений. Это может быть сделано с помощью метода &amp;lt;tt&amp;gt;join()&amp;lt;/tt&amp;gt; который возвращает строку с элементами, перемежая их некоторой строкой-разделителем. Разделитель может быть передан в качестве параметра; по умолчанию это строка &amp;quot;, &amp;quot; (запятая с пробелом):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var my_array = [1, 2, 3];&lt;br /&gt;
var my_list = new list;&lt;br /&gt;
my_array.each() { |x| my_list.push(x); };&lt;br /&gt;
&lt;br /&gt;
var s_list = '[' + my_list.join() + ']'; // slist = &amp;quot;[1, 2, 3]&amp;quot;&lt;br /&gt;
//дальнейшая работа с s_list (например, вывод на экран)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это простой пример использования метода &amp;lt;tt&amp;gt;join&amp;lt;/tt&amp;gt;, при котором он используется для создания текстового представления содержимого списка. Для удобства и указания границ, добавляются квадратные скобки. Вот другой пример использования метода, на этот раз, для сохранения некоторого списка ресурсов которые используются приложением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
//подготавливаем список опций&lt;br /&gt;
var search_dirs = new list;&lt;br /&gt;
search_dirs.push('http://example.com/db/');&lt;br /&gt;
search_dirs.push('ftp://example.org/db/');&lt;br /&gt;
search_dirs.push('diss:/media/storage/~user1/');&lt;br /&gt;
&lt;br /&gt;
//открываем поток файла настроек&lt;br /&gt;
var config = stream.open('diss:/etc/sample.conf', stream.create | stream.write);&lt;br /&gt;
config.write('dbpath=' + search_dirs.join(';')); //записываем строку конфигурации&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как мы видим, сначала происходит заполнение списка значениями URL ресурсов, а затем полученный список записывается в ''поток''. Класс потока это абстракция, предоставляемая ядром Диптауна для доступа к различным ресурсам. Все что необходимо сделать чтобы использовать потоки, это создать инстанцию класса &amp;lt;tt&amp;gt;stream&amp;lt;/tt&amp;gt; с момощью конструктора &amp;lt;tt&amp;gt;create()&amp;lt;/tt&amp;gt;, указав требуемый URL потока и задать режим доступа к ресурсу (с помощью констант класса). При этом, на пользовательском уровне, требуется только писать в поток и читать из него; пользователь не должен беспокоиться, каким образом будет происходить реальная работа с данными на низком уровне — этим занимается ядро. Если же ядро не поддерживает конкретный тип потока — будет сгенерировано исключение.&lt;br /&gt;
&lt;br /&gt;
Для последуюезго разбора полученной строки, можно применять уже известный нам метод &amp;lt;tt&amp;gt;split()&amp;lt;/tt&amp;gt; и регулярные выражения.&lt;br /&gt;
&lt;br /&gt;
В заключение, приведем две реализации кода, вычисляющего первую сотню простых чисел. Одну реализацию мы напишем с использованием списков, а другую с помощью массивов. В нижеприведенном коде активно применяются циклы, так что, для лучшего понимания материала, Читателю предлагается забежать вперед и [[Основные синтаксические конструкции#Циклы|ознакомиться]] с ними. Итак, первая реализация:&lt;br /&gt;
&amp;lt;source lang=kpp line=1&amp;gt;&lt;br /&gt;
function GetPrimes() {&lt;br /&gt;
    var primes = new list;&lt;br /&gt;
    var x = 1;&lt;br /&gt;
    while (primes.size() &amp;lt; 100) {&lt;br /&gt;
        x++; //текущий кандидат&lt;br /&gt;
        var is_prime = true;&lt;br /&gt;
        for (var p = primes.begin(); p != primes.end(); ++p)&lt;br /&gt;
            if (x % p.object == 0) {&lt;br /&gt;
                is_prime = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        if (is_prime) {&lt;br /&gt;
            primes.push(x); //помещаем число в список&lt;br /&gt;
            print(&amp;quot;the #{primes.size()} prime number is #{x}\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм поиска такой: берем число и начинаем вычислять остаток от его деления на все элементы нашего списка, то есть, выполняем попытку разложения числа на простые сомножители. Если число делится без остатка хотя бы на один из сомножителей (условие в строке 9), то число не является простым. Следовательно проверять его дальше не имеет смысла. Мы прекращаем цикл с помощью оператора &amp;lt;tt&amp;gt;'''break'''&amp;lt;/tt&amp;gt; и сбрасываем флаг ''is_prime''. Если после перебора всех множителей, флаг остался установленным, то это значит, что текущее число (переменная ''x'') является и вправду простым: его мы помещаем в конец списка. Так продолжается до тех пор, пока в списке не окажится 100 элементов. Для проверки размера списка (условие [[Основные синтаксические конструкции#Цикл while|цикла &amp;lt;tt&amp;gt;'''while'''&amp;lt;/tt&amp;gt;]] в строке 4) применяется метод &amp;lt;tt&amp;gt;size()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на то, каким образом реализуется перебор значений списка. Для этой операции применяется ''итератор''. Итератор — это специальный класс, объекты которого используются в качестве указателей на элементы контейнеров. Как правило, итераторы имеют методы для перемещения по контейнеру (операторы &amp;lt;tt&amp;gt;++&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;--&amp;lt;/tt&amp;gt;) и некоторое свойство, позволяющее обратиться к текущему элементу, на который указывает итератор. В случае класса &amp;lt;tt&amp;gt;list_iterator&amp;lt;/tt&amp;gt;, это свойство &amp;lt;tt&amp;gt;object&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для перебора списка применяется [[Основные синтаксические конструкции#Цикл for|цикл &amp;lt;tt&amp;gt;'''for'''&amp;lt;/tt&amp;gt;]]. В инициализаторе цикла была создана управляющая переменная ''p'', которая и является инстанцией итератора (класса &amp;lt;tt&amp;gt;list_iterator&amp;lt;/tt&amp;gt;). Список имеет специальный метод &amp;lt;tt&amp;gt;begin()&amp;lt;/tt&amp;gt;, который создает инстанцию итератора и устанавливает ее на свое начало. &lt;br /&gt;
&lt;br /&gt;
Метод &amp;lt;tt&amp;gt;end()&amp;lt;/tt&amp;gt; возвращает значение специального итератора, который всегда указывает на конец списка. Его нельзя двигать, и служит он только для одной цели — для проверки граничных условий. Важно понимать, что под концом списка понимается не последний его элемент, а именно конец — нечто, следующее за конечным элементом. Таким образом, условие в цикле &amp;lt;tt&amp;gt;'''for'''&amp;lt;/tt&amp;gt; проверяет, есть ли еще элементы &amp;quot;справа&amp;quot; от итератора ''p''. Если условие истинно, значит итератор находится где то в середине списка и можно продолжать итерации; если ложно — значит предыдущий элемент был последним и больше элементов в списке нет.&lt;br /&gt;
&lt;br /&gt;
Вот второй способ реализации того же участка кода, но как уже говорилось выше, с помощью массива:&lt;br /&gt;
&amp;lt;source lang=kpp line=1&amp;gt;&lt;br /&gt;
function GetPrimes() {&lt;br /&gt;
    var primes = new array;&lt;br /&gt;
    primes.resize(100); //задаем размер массива&lt;br /&gt;
    var x = 1;&lt;br /&gt;
    var count = 0; //текущее число найденных простых&lt;br /&gt;
    while (count &amp;lt; 100) {&lt;br /&gt;
        x++; //текущий кандидат&lt;br /&gt;
        var is_prime = true;&lt;br /&gt;
        for (var i = 0; i &amp;lt; count; ++i)&lt;br /&gt;
            if (x % primes[i] == 0) {&lt;br /&gt;
                is_prime = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        if (is_prime) {&lt;br /&gt;
            primes[count++] = x; //помещаем число в массив&lt;br /&gt;
            print(&amp;quot;the #{count} prime number is #{x}\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку мы заранее знаем размер массива, который нам может потребоваться (100 элементов), мы можем выделить всю необходимую память одним махом. Это будет намного быстрее, чем выделять память по мере итераций, ведь копирование элементов массива в новое место будет произведено всего один раз. Для изменения размера массива применяется метод &amp;lt;tt&amp;gt;resize()&amp;lt;/tt&amp;gt;. В качестве аргумента метод принимает число — размер массива, который мы желаем получить. Если желаемый размер меньше текущего, то массив будет урезан до нового размера (элементы, оказавшиеся &amp;quot;за бортом&amp;quot;, будут выброшены). Если больше — массив будет расширен, причем все старые элементы останутся невредимыми на своих местах, а новые примут значение &amp;lt;tt&amp;gt;'''null'''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввиду того, что размер массива был задан заранее, проверять конечное условие по количеству элементов нельзя (оно уже равно 100). Поэтому, была введена новая переменная ''count'', в которой мы будем хранить текущее количество найденных простых чисел, а поскольку размещаем мы их строго последовательно, то и индекс последнего найденного простого числа всегда будет известен (какой?).&lt;br /&gt;
&lt;br /&gt;
При работе с массивами никакие итераторы нам не нужны — у нас есть индексы. Для доступа к некоторому элементу массива нам надо знать его индекс. Сама операция получения значения элемента, осуществляется через оператор индексного доступа &amp;lt;tt&amp;gt;[]&amp;lt;/tt&amp;gt;. То есть, в нашем случае, &amp;lt;tt&amp;gt;primes[0]&amp;lt;/tt&amp;gt; будет соответствовать первому элементу массива, &amp;lt;tt&amp;gt;primes[1]&amp;lt;/tt&amp;gt; — второму, и т. д. (не забывайте, что индексация производится с нуля).&lt;br /&gt;
&lt;br /&gt;
Сам алгоритм поиска чисел, практически один-в-один повторяет алгоритм, описанный в предыдущем примере, так что подробно мы его описывать не будем. В качестве упраждения на внимательность, вы можете попытаться найти все отличия. &lt;br /&gt;
&lt;br /&gt;
Давайте лучше попробуем проанализировать оба решения и выяснить, какое из решений более оптимально для поставленной задачи. Для того, чтобы ответить на этот вопрос, необходимо прикинуть общее количество операций добавления элементов в массив и сравнить с количеством операций доступа к массиву, или операций выборки. &lt;br /&gt;
&lt;br /&gt;
В нашем алгоритме, мы видим следующее: на каждое число-кандидат, мы вынуждены пробегать все элементы коллекции и выполнять над ними некоторые действия. Поскольку, выборка элементов происходит строго последовательно, особой разницы между массивом и списком тут нет. Операция добавления элементов также происходит строго в конец коллекции и довольно редко. В целом, учитывая произведенные оптимизации (вроде предустановки размера массива), можно заключить что сложности алгоритмов примерно равны. &lt;br /&gt;
&lt;br /&gt;
Не забывайте, что это связано с жестко заданными условиями работы алгоритма! Представьте, если бы нам требовалось реализовать алгоритм, выбирающий элементы из коллекции не последовательно, а некоторым случайным, или почти случайным образом. В случае списка, итераторы нам уже не помогли бы. Пришлось бы опять прибегать к операции последовательного прохождения цепочки для отыскания нужного элемента. В таких условиях, по скорости массив был бы далеко впереди списка. Чуть изменим условия — и все может перевернуться вверх дном. Предположим, что реализуется алгоритм, при котором элементы добавляются не в конец, а в середину коллекции, да еще заранее не известно, какой может быть размер коллекции (так что невыгодно применять &amp;lt;tt&amp;gt;resize()&amp;lt;/tt&amp;gt;). В таких условиях, уже список будет выигрывать у массива.&lt;br /&gt;
&lt;br /&gt;
Получается, что от используемого алгоритма (а так же от четкости поставленных условий), во многом зависит то, насколько эффективен будет тот или иной контейнер. Вот почему при проектировании программ, требуется тщательным образом подходить к вопросу анализа вычислительной сложности алгоритмов и выбирать более подходящие средства для их реализации. Но как всегда, идеальных решений не бывает — выбор будет лежать где то посередине между ресурсоемкостью и производительностью.&lt;br /&gt;
&lt;br /&gt;
== Хеши ==&lt;br /&gt;
&lt;br /&gt;
Хеши являются еще одной формой контейнеров. Подобно массивам и спискам, они могут хранить в себе другие объекты, однако они отличаются и от тех и от других. Для доступа к элементу списка, необходимо использовать итераторы, в то время как для ссылки на элемент массива применяются индексы — числа, которые соответствуют номеру элемента в массиве. &lt;br /&gt;
&lt;br /&gt;
В этом отношении, хеши похожи на массивы, однако, для доступа к элементу используются не индексы, а совершенно произвольные объекты; или если перефразировать наоборот, то: для доступа к некоторому элементу хеша применяются индексы, которые могут быть представлены любым объектом: числом, строкой либо экземпляром пользовательского класса.&lt;br /&gt;
&lt;br /&gt;
Хеши обычно применяются там, где требуется организовать связь нескольких объектов. При этом, первому объекту-индексу, который в хешах называется ''ключом'', ставится в соответствие другой объект — ''значение''. Хеши хранят множество таких пар ключ-значение и, подобно спискам и массивам, предоставляют удобные методы для работы со своим содержимым.&lt;br /&gt;
&lt;br /&gt;
Чаще всего, хеши применяются для связи текстовых строк или для организации ''ассоциативных массивов'', у которых в качестве индекса применяется строка. Приведем несколько примеров применения хешей. Допустим, мы хотим поставить в соответствие названию дня недели его порядковый номер. То есть, строке &amp;quot;понедельник&amp;quot; должно соответствовать число 1, &amp;quot;вторнику&amp;quot; — 2 и так далее. Конечно, эту задачу можно решить и традиционным способом, например с помощью массивов:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
const weekdays = ['понедельник', 'вторник', 'среда', 'четверг',&lt;br /&gt;
                  'пятница', 'суббота', 'воскресенье'];&lt;br /&gt;
&lt;br /&gt;
function int WeekDay2DayNr(const string weekday) {&lt;br /&gt;
    var result; //результат (динамическая переменная)&lt;br /&gt;
    weekdays.each_pair() { |nr, day| &lt;br /&gt;
        if (day == weekday) { &lt;br /&gt;
            result = nr + 1; &lt;br /&gt;
            break; &lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
    if (result) //если переменной присвоено значение&lt;br /&gt;
        return result; //возвращаем его&lt;br /&gt;
    else //иначе, сообщаем об ошибке&lt;br /&gt;
        throw EInvalidCall.create('указанная строка не является днем недели');&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом примере, мы заводим массив ''weekdays'', в который помещаем названия всех дней недели. &lt;br /&gt;
&lt;br /&gt;
В функции &amp;lt;tt&amp;gt;WeekDay2DayNr()&amp;lt;/tt&amp;gt; мы перебираем все элементы массива и сравниваем их с контрольной строкой. Если происходит совпадение, то мы сохраняем текущее значение индекса (''nr'') в динамическую переменную ''result'' и прерываем цикл с помощью оператора &amp;lt;tt&amp;gt;'''break'''&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Далее, с помощью условного оператора проверяется, было ли переменной ''result'' присвоено некоторое значение. Если было, то оно возвращается как результат функции; если нет (то есть, оно по прежнему равно &amp;lt;tt&amp;gt;'''null'''&amp;lt;/tt&amp;gt;) — генерируется [[Идеология языка#Понятие исключения|исключение]].&lt;br /&gt;
&lt;br /&gt;
Данная функция будет исправно работать, однако в реальных условиях, при большом количестве элементов массива, операция поиска перебором может стать очень медленной. В таком случае, наилучшим решением, будет использование хешей. Перепишем предыдущую функцию так, чтобы она использовала хеш вместо массива: &lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
const weekdays = {'понедельник' =&amp;gt; 1, 'вторник' =&amp;gt; 2, 'среда'   =&amp;gt; 3, &lt;br /&gt;
                  'четверг'     =&amp;gt; 4, 'пятница' =&amp;gt; 5, 'суббота' =&amp;gt; 6, &lt;br /&gt;
                  'воскресенье' =&amp;gt; 7};&lt;br /&gt;
&lt;br /&gt;
function int WeekDay2DayNr(const string day) {&lt;br /&gt;
    try {&lt;br /&gt;
        return weekdays[day];&lt;br /&gt;
    } catch (ERangeError e) {&lt;br /&gt;
        throw EInvalidCall.create('указанная строка не является днем недели');&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Посмотрите, насколько проще стал код. А главное, он стал намного быстрее! Хеши позволяют значительно повысить скорость доступа к информации за счет того, что вместо обычного перебора, применяются альтернативные методы. Если говорить кратко, то происходит операция &amp;quot;перемешивания&amp;quot; ключа, которая осуществляется с помощью [http://ru.wikipedia.org/wiki/Хеш-функция хеш-функции], в результате которой получается некоторое значение — хеш ключа. Это значение уже используется для выборки искомого объекта. Скорость операции возрастает за счет того, что от медленной операции сравнения ключей, мы переходим к быстрой операции сравнения хешей. Хеши изначально проектируются так, чтобы их было легко сравнивать; в то же время, они должны быть различными для разных значний ключа. Более подробно, про хеши и хеширование, можно [http://ru.wikipedia.org/wiki/Хеш-таблица почитать на Википедии].&lt;br /&gt;
&lt;br /&gt;
Для объявления хеш-таблицы ''weekdays'' применяется специальный синтаксис, встроенный в язык К++: необходимо в фигурных скобрах перечислить пары ключ-значение, отделяя их запятой. Для связи ключа и значения в паре, применяется оператор соответствия (&amp;lt;tt&amp;gt;=&amp;gt;&amp;lt;/tt&amp;gt;). Подобно массивам, объекты хеш-таблиц могут объявляться где угодно в коде, будь то инициализатор переменной, либо фактический параметр в коде вызова функции. В нашем случае, объект создается в инициализаторе [[константы]], которая будет иметь тип &amp;lt;tt&amp;gt;hash&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Разумеется, для заполнения хеша можно применять и обычную операцию создания объекта, с последующим вызовом методов &amp;lt;tt&amp;gt;insert()&amp;lt;/tt&amp;gt; для добавления информации в таблицу:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
var my_config = new hash;&lt;br /&gt;
my_config.insert('verbosity', 3);&lt;br /&gt;
my_config.insert('user name', 'nobody');&lt;br /&gt;
my_config.insert('password', 'y&amp;amp;5#3Eff_');&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Для заполнения хеша можно применять так же оператор индексного доступа:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
my_config[:path] = 'diss:/etc/myconf';&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Между вызовом метода &amp;lt;tt&amp;gt;insert()&amp;lt;/tt&amp;gt; и использованием оператора &amp;lt;tt&amp;gt;[]=&amp;lt;/tt&amp;gt; есть небольшая разница. Она заключается в том, что случае, если в хеше уже присутствует пара значений с тем же ключом, то метод &amp;lt;tt&amp;gt;insert()&amp;lt;/tt&amp;gt; сгенерирует [[Идеология языка#Понятие исключения|исключение]], в то время как оператор &amp;lt;tt&amp;gt;[]=&amp;lt;/tt&amp;gt; заменит старое значение на новое. Существует так же метод &amp;lt;tt&amp;gt;replace()&amp;lt;/tt&amp;gt;, который ведет себя подобно вышеозначенному оператору:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
my_config[:enabled] = 'true';&lt;br /&gt;
my_config.replace('enabled', 'true');&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Хеши, подобно массивам и спискам, обладают набором методов для перебора их содержимого, однако смысл некоторых методов немного отличается от своих аналогов в массивах:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
weekdays.each() { |day, nr| print(&amp;quot;key: #{day}, value: #{nr}\n&amp;quot;); };&lt;br /&gt;
var keys = weekdays.keys();&lt;br /&gt;
keys.each() { |key| println(key); };&lt;br /&gt;
weekdays.values().each() { |value| print(value as string + ' '); };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом примере используются три различных метода, которые вызывают блок с парой параметров ключ-значение (метод &amp;lt;tt&amp;gt;each()&amp;lt;/tt&amp;gt;), либо возвращают массивы ключей и значений (методы &amp;lt;tt&amp;gt;keys()&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;values()&amp;lt;/tt&amp;gt; соответственно). Во второй и третьей строках, показывается способ отображения всех ключей хеша, с помощью метода &amp;lt;tt&amp;gt;keys()&amp;lt;/tt&amp;gt; и промежуточной переменной. В последней строке вызовы методов совмещены в одной конструкции. Для наглядности приведем вывод, каким он должен был бы быть, при выполнении этого кода (подразумевается использование хеша ''weekdays'' из предыдущих примеров):&lt;br /&gt;
&lt;br /&gt;
 key: понедельник, value: 1&lt;br /&gt;
 key: вторник, value: 2&lt;br /&gt;
 key: среда, value: 3&lt;br /&gt;
 key: четверг, value: 4&lt;br /&gt;
 key: пятница, value: 5&lt;br /&gt;
 key: суббота, value: 6&lt;br /&gt;
 key: воскресенье, value: 7&lt;br /&gt;
 понедельник&lt;br /&gt;
 вторник&lt;br /&gt;
 среда&lt;br /&gt;
 четверг&lt;br /&gt;
 пятница&lt;br /&gt;
 суббота&lt;br /&gt;
 воскресенье&lt;br /&gt;
 1 2 3 4 5 6 7&lt;br /&gt;
&lt;br /&gt;
'''Примечание 1:''' Здесь мы перечислили выводимые значения по порядку. Однако, на практике такого не случается. За счет операции перемешивания, порядок следования элементов теряется. Поэтому, если требуется сохранить порядок, необходимо либо где-то записывать очредность ключей, либо сортировать их при обработке. Например, код обработки элементов хеша в порядке очередности ключей может выглядеть примерно так:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
my_hash.keys().sort().each() { |key| print(&amp;quot;key: #{key}, value: #{my_hash[key]}\n&amp;quot;); };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Примечание 2:''' Как уже было сказано выше, хеши позволяют использовать объекты любого типа как в качестве ключа, так и в качестве значения. Для того, чтобы можно было указывать в качестве ключа объект пользовательского типа, он должен иметь реализацию метода &amp;lt;tt&amp;gt;hash()&amp;lt;/tt&amp;gt;, не принимающего параметров и возвращающего объект класса &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;. Дополнительно, класс объектов ключа должен предоставлять операторы сравнения и присвоения (&amp;lt;tt&amp;gt;==&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;=&amp;lt;/tt&amp;gt;). На объекты, содержащиеся в значениях, никаких ограничений не накладывается. &lt;br /&gt;
&lt;br /&gt;
Таким образом, реализация класса ключей должена выглядеть примерно так:&lt;br /&gt;
&amp;lt;source lang=kpp&amp;gt;&lt;br /&gt;
class MyClass {&lt;br /&gt;
public:&lt;br /&gt;
    export const function int hash() {&lt;br /&gt;
        /* здесь идет код вычисления хеша */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const operator bool == (const MyClass x) {&lt;br /&gt;
        /* код сравнения инстанций */ &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    operator MyClass = (const MyClass src) {&lt;br /&gt;
        /* код присвоения */ &lt;br /&gt;
        return this;&lt;br /&gt;
    }&lt;br /&gt;
    /* другие методы */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Указатели ==&lt;/div&gt;</summary>
		<author><name>Kamillen</name></author>	</entry>

	</feed>