Во Флэш МХ
имеется встроенная библиотечка объектов-контейнеров.
Обычно в библиотеках контейнеров (такой, например, как
стандартная библиотека С++) можно найти контейнеры,
оптимизированные для всех случаев жизни - по скорости
выполнения самых разных операций, по занимаемой памяти и
пр. Создатели Флэша поступили проще: они, резонно
полагая, что главная оптимизация состоит в использовании
заранее скомпилированного компонента, предлагают нам для
большинства задач один универсальный контейнер. Это
класс Array. Однако, судя
по названию, основной его задачей является играть роль
массива, поэтому посмотрим,
как он с этой задачей справляется.
Массив: различные
способы создания
Сейчас мы
рассмотрим все способы создания
массива, причем начнем с устаревших, а закончим
самыми совершенными и удобными.
Эмуляция массива
Во Флэше
версии 4 настоящих массивов
не было и выкручиваться приходилось примерно так:
for (var i=0; i<5; i++) set("a"
+ i, 5*i + 1);
// Обращаемся к конкретному элементу
trace("a2 = " + a2);
i = 4;
// Обращаемся по номеру, записанному в переменной
trace("a" + i + " = " + eval("a" + i));
Выводит этот
код (который, правда, напрямую перенести во Флэш 4
нельзя - мы хотя и не использовали
массивы, но зато применили ряд других вещей,
которых в четвертой версии Флэша не было) следующие
строчки:
a2 = 11
a4 = 21
Во Флэш МХ
делать подобные вещи смысла не имеет. (Хотя генерация
серий имен, бывает, применяется при создании наборов
похожих клипов.) Гораздо лучше пользоваться встроенным
объектом типа Array.
Создание массива с
помощью new
Создать
объект типа Array можно,
например, так (на выбор целых три способа):
a_array = new
Array(2,3,"AAA",4,8);
b_array = new Array(10);
c_array = new Array();
// Обращаемся к конкретному элементу
trace("a_array[1] = " + a_array[1]);
i = 3;
// Обращаемся по номеру, записанному в переменной
trace("a_array[" + i + "] = " + a_array[i]);
c_array[7] = 9;
// Длину массива можно узнать, обратившись к
специальному
// свойству length
trace("a_array.length = " + a_array.length);
trace("b_array.length = " + b_array.length);
trace("c_array.length = " + c_array.length);
Выводит
этот код следующее:
a_array[1] = 3
a_array[3] = 4
a_array.length = 5
b_array.length = 10
c_array.length = 8
Посмотрим
также, какие переменные заведены в программе (надо после
запуска на исполнение нажать Ctrl+Alt+V). Получим:
Level #0:
Variable _level0.$version = "WIN 6,0,21,0"
Variable _level0.a_array = [object #1, class
'Array'] [
0:2,
1:3,
2:"AAA",
3:4,
4:8
]
Variable _level0.b_array = [object #2, class
'Array'] []
Variable _level0.c_array = [object #3, class
'Array'] [
7:9
]
Variable _level0.i = 3
Итак, с
точки зрения С++ или Java класс
Array имеет три разных конструктора. Один из них
- тот, в который передается больше одного аргумента -
создает массив из всех
аргументов, переданных в конструктор. Второй - с одним
числовым аргументом - создает
массив указанной длины, хотя и незаполненный.
Наконец, третий, вовсе без аргументов, просто создает
массив с нужным именем.
Длина его первоначально нулевая. Однако, если мы
попытаемся записать что-либо в ячейку
массива, которая еще не
создана, массив немедленно
расширится до нужного размера.
Заодно, на
этих примерах мы убедились, что во Флэш МХ, как и в C++
и Java, массивы нумеруются
с нуля.
Обратите
внимание, что в названии наших
массивов мы используем суффикс
_array. Это позволяет нам
пользоваться контекстной подсказкой: сразу после
постановки точки вслед за именем переменной с таким
суффиксом на экране появится всплывающее меню, из
которого мы сможем выбрать нужный метод (или поле)
класса Array.
Создание массива с
помощью квадратных скобок
Однако
необязательно создавать массивы
именно с помощью оператора new.
Есть и другой способ, который вы, возможно, сочтете
более удобным. Оказывается, перечисление элементов
массива в квадратных
скобках через запятую полностью эквивалентно его
созданию при помощи new с
использованием первого конструктора (в котором также
перечисляются элементы массива).
Вот пример:
i = 1
a_array = [2, i, 0, 5, null];
i = 3;
// Обращаемся по номеру, записанному в переменной
trace("a[" + i + "] = " + a_array[i]);
// Выводим весь массив целиком
trace(a_array);
На выходе
мы получим:
a[3] = 5
2,1,0,5,null
Все как и
следовало ожидать. Заметьте, что функция
trace легко смогла вывести
весь массив целиком - на
самом деле эта функция справится и с любым другим
объектом, у которого определен метод
toString(). То есть записи
trace(a_array) и
trace(a_array.toString())
эквивалентны.
А что будет,
если записать нечто в индекс, который находится за
пределами массива?
Оказывается, все произойдет наилучшим образом:
массив расширится как раз
до тех пор, чтобы вместить новое поле. Впрочем, индекс
должен быть целым и неотрицательным. При попытке
что-либо прочесть из нецелого или отрицательного индекса
мы получим undefined.
Попытка записи в нецелый индекс за границей
массива дает смешной
результат: массив
расширяется, но просто так, при обычном переборе нецелый
индекс не виден. К нему можно обратиться лишь напрямую.
Вот примеры всего этого: запускаем следующий код
a = [12, 22];
a[5] = 77;
trace("a = " + a);
b = [33, 44, 55];
b[-1] = 88;
trace("b = " + b);
trace("b[1.7] = " + b[1.7]);
trace("b[Math.ceil(1.7)] = " + b[Math.ceil(1.7)]);
trace("b[8] = " + b[8]);
a[9.81] = 333;
trace("a = " + a);
trace("a[9.81] = " + a[9.81]);
и получаем:
a = 12,22,,,,77
b = 33,44,55
b[1.7] =
b[Math.ceil(1.7)] = 55
b[8] =
a = 12,22,,,,77,,,,
a[9.81] = 333
Если вас
заинтересовало поведение массива
для нецелых ключей, вы можете просмотреть чуть дальше
параграф о нечисловых ключах. Вы сразу поймете причины
такого поведения.
Многомерные массивы,
создание массива "на лету"
А вот как
делаются во Флэш МХ многомерные
массивы:
i = 7;
a_array = [[i, i+1], [i+2, i+3], [1, 2], [3, 4, 5,
6], "aaaaaaa"];
// Обращаемся к конкретному элементу
trace("a_array[1][1] = " + a_array[1][1]);
// Выводим подмассив
trace("a_array[3] = " + a_array[3]);
// Выводим список всех элементов субмассивов
trace("a_array = " + a_array);
// Создание массива "на лету"
trace([100, 23, -3]);
Приведенный
здесь код выводит:
a_array[1][1] = 10
a_array[3] = 3,4,5,6
a_array = 7,8,9,10,1,2,3,4,5,6,aaaaaaa
100,23,-3
Из этого
примера можно заключить, что
многомерный массив
во Флэше не является специальным случаем
массива (как и в C/C++).
Более того, вовсе не каждый элемент
многомерного
массива в свою очередь
должен являться массивом.
Просто каждый элемент массива
может быть объектом любого типа - примитивного или нет.
В том числе - другим массивом.
Однако с
этой точки зрения строчка
a_array = [[i, i+1], [i+2,
i+3], [1, 2], [3, 4, 5, 6], "aaaaaaa"];
выглядит
так, как будто субмассивы были созданы прямо по ходу
дела - вместо записи вида
temp1_array = [i, i+1];
temp2_array = [i+2, i+3];
temp3_array = [1,2];
temp4_array = [3, 4, 5, 6];
a_array = [temp1_array, temp2_array, temp3_array,
temp4_array,
"aaaaaaa"];
То есть
возникает впечатление, что создать
массив можно прямо "на лету", внутри любой части
какого-нибудь выражения. Это подтверждает последняя
строчка вышеприведенного примера:
trace([100, 23, -3]); действительно выводит
содержание созданного прямо внутри функции
trace
массива. Более того,
следующий код, на первый взгляд кажущийся безумным,
также компилируется и работает:
trace(([1, 2, 3])[2])
trace([1, 2, 3][0])
trace([10][0])
trace([3,7, 2, -1].sort())
Выводит он
3
1
10
-1,2,3,7
В первой
строчке мы на всякий случай взяли в круглые скобки
создание массива. Вторая
строчка подтверждает, что и без круглых скобок Флэш
может отличить, где скобки квадратные означают создание
массива, а где - обращение
к его ячейке по номеру. Третья строчка подтверждает, что
это различие состоит вовсе не в количестве чисел в
скобках, а в том, стоит ли слева от квадратных скобок
подвыражение, представляющее собой
массив, или же нет (в последнем случае
массив создается). Наконец,
четвертая строка - демонстрация того, что с только что
созданным массивом можно
производить такие же операции (например, вызов методов),
как и с массивом, созданным
заранее.
Напоследок
- нечто, более напоминающее загадку, чем имеющее
какой-либо практический смысл. Что выводит следующая
строка: trace([1, 2, 3, 4, 5][2,
0, 7, 3]);? Если ответ не сразу приходит вам в
голову, то попробуйте разгадать, почему в результате
выполнения этой строчки печатается 4? Разгадка же
состоит в том, что Флэш лишь первые квадратные скобки
воспринимает как указание создать
массив. Вторые (увидев, что
массив слева от них уже стоит) он воспринимает
как обращение к ячейке массива.
А выражение внутри них - как определяющее индекс этой
ячейки. Вычислив это выражение (по правилам пользования
операцией "запятая"), он получает, что оно равно самому
правому числу в последовательности, то есть 3. А элемент
массива, к которому мы
обращаемся, имеющий индекс 3, - это и есть четверка.
Сортировка
Алгоритмы
сортировки - это одна из
тех вещей, которые изучают все программисты, и которыми
затем практически никогда не пользуются (потому что все
современные библиотеки функций и классов предоставляют
готовые реализации сортировки).
Предоставляет свою реализацию
сортировки и Флэш МХ. Посмотрим, хороша ли она, и
если да, то чем.
Сортировка чисел в
массиве
Попробуем
отсортировать числовой массив.
a_array = [3, 0.5, +3, -3,
1e12, 1e-12, 12, 112];
a_array.sort();
trace(a_array);
На выходе
получаем:
-3,0.5,1000000000000,112,12,1e-12,3,3
Видно, что
метод sort() (в отличие от
многих методов массива, с
которыми мы познакомимся позднее) действительно изменяет
массив. Но что это? В каком
порядке он расставил числа? Мы наткнулись на неприятную
особенность этого метода: по умолчанию он преобразует
все элементы массива в
строки и лишь потом
пытается отсортировать. Чтобы получить правильно
отсортированный массив
чисел, нам придется написать что-то вроде
a_array.sort(function(a,b){return a-b;});
Тогда на
выходе действительно будет
-3,1e-12,0.5,3,3,12,112,1000000000000
О том, что
на самом деле означает строчка
a_array.sort(function(a,b){return a-b;});, вы
узнаете чуть позднее. Впрочем, уже ясно, что для
сортировки по убыванию надо
написать
a_array.sort(function(a,b){return b-a;});. Также
приятное известие состоит в том, что в ActionScript 2
рассматриваемая проблема решена - в метод
sort можно передавать
набор управляющих флагов, один из которых
(Array.NUMERIC) командует
трактовать содержимое массива
как числа.
Сортировка строк
Но зато с
сортировкой
строк проблем у нас не
будет. Пишем, например:
s_array = ["bulb", "bulb.",
"c", "CC", "ground"];
s_array.sort();
trace(s_array);
и получаем
в результате
CC,bulb,bulb.,c,ground
А как нам
отсортировать массив в
обратном направлении (по убыванию)? В ActionScript 2
можно передать флаг
Array.DESCENDING. Нам же придется сделать
a_array.reverse();
Сортировка объектов по
полю
Если в
массиве лежат объекты, то
их можно отсортировать по значению любого из полей.
Конечно, чтобы такая операция имела смысл, указанное
поле должно присутствовать практически во всех
сортируемых объектах. Но, как это водится во Флэш МХ,
сообщений об ошибках мы не получим, даже если этого поля
нет ни в одном из объектов. Как бы то ни было, вот
пример такой сортировки
(выполняющейся с помощью специального метода
Array.sortOn):
a_array = [{name:"aa",
info:112}, {name:"bb", info: 23},
{name: "ba", info:1e-5}];
a_array.sortOn("name");
for (var i=0; i<a_array.length; i++){
trace("{" + a_array[i].name + ", " + a_array[i].info + "}");
}
trace("---------");
a_array.sortOn("info");
for (var i=0; i<a_array.length; i++){
trace("{" + a_array[i].name + ", " + a_array[i].info + "}");
}
На выходе
получим:
{aa, 112}
{ba, 0.00001}
{bb, 23}
---------
{ba, 0.00001}
{aa, 112}
{bb, 23}
Видим, что
непорядок с сортировкой
чисел имеет место и здесь. Правда, исправить его будет
чуть сложнее, чем при сортировке
обычных чисел, а не объектов. Ведь функция
sortOn не имеет
дополнительных параметров - в нее нельзя передать
функцию сортировки.
Разбираться с этой неприятностью мы будем уже в
следующем подпараграфе. А пока заметим, что если у
какого-то из объектов поля name
(или info при
сортировке по
info) не будет, он попадет
в начало массива, поскольку
при обращении к этому полю будет возвращаться
undefined. В свою очередь,
при преобразовании undefined
в строчку, получится пустая строка,
которая "меньше" любой другой
строки. То же самое случится, если в
массиве попадется,
например, обыкновенное число вместо объекта. Главное,
чтобы вместо объекта в массиве
не стояла строка. По
неизвестным причинам Флэш падает при попытке запустить
сортировку такого
массива в интегрированном в
среду плеере. (Хотя во внешнем плеере ничего страшного
не происходит; так что можно обойтись без специальных
проверок на этот счет.)
Сортировка с помощью
собственной функции
Мы уже
слегка касались возможности сортировать
массив при помощи функции
упорядочивания, написанной самостоятельно. Она
совершенно необходима в ActionScript первой версии для
правильной сортировки
численных массивов.
Применять ее можно и для любых других целей. Сейчас мы
приведем еще один пример, по синтаксису несколько
отличающийся от предыдущего. Это связано с тем, что во
Флэше есть два разных способа определять функции.
Подробнее вы узнаете о них в следующей лекции, а пока
можете действовать в соответствии с одним из приведенных
образцов. Итак, что же принимает и что возвращает
функция упорядочивания? Принимает она два элемента
массива - это могут быть
элементы объектных или примитивных типов. Что же
касается возвращаемого значения, то здесь имеется некая
разноголосица. В руководстве пользователя Флэш МХ и в
онлайн-документации вы прочтете, что эта функция должна
возвращать 1, если вы хотите, чтобы первый аргумент в
сортированном массиве стоял
позже второго, -1 - если раньше, и 0 - если порядок их
безразличен. На самом же деле достаточно, чтобы в первом
случае выдавалось любое положительное число, а во втором
- любое отрицательное. Именно поэтому в случае
сортировки чисел достаточно
возвращать их разность. А мы сейчас воспользуемся
собственной функцией упорядочивания, чтобы правильно
отсортировать массив из
предыдущего примера по полю info.
Вот как это делается:
a_array = [{name:"aa",
info:112}, {name:"bb", info: 23},
{name: "ba", info:1e-5}];
// Это еще один способ определить функцию;
// подробнее он будет описан позднее
function sortFunc(o1, o2){return o1.info - o2.info;}
a_array.sort(sortFunc);
for (var i=0; i<a_array.length; i++){
trace("{" + a_array[i].name + ", " + a_array[i].info + "}");
}
На выходе
получаем:
{ba, 0.00001}
{bb, 23}
{aa, 112}
Вот теперь
порядок правильный!
Эмуляция стека,
очереди и хэш-таблицы
Как мы уже
говорили, массив во Флэше
многофункциональный и позволяет эмулировать многие
другие структуры данных. Специальных методов в
массиве нет лишь для
эмуляции хэш-таблицы - но
своего рода хэш-таблицей
является во Флэше любой объектный тип, так что и с этой
задачей массив справляется.
Давайте рассмотрим по порядку, что и как можно
эмулировать.
Эмуляция списка
Функциональность списка
эмулируется при помощи методов
splice, slice и
concat. Собственно говоря,
специфичной для списка
является легкость удаления и вставки элементов, а также
простота перемещения маркеров начала и конца
списка. (Вообще-то
изменение положений начала и конца сводится к удалению
элементов, и мы отдельно упомянули об этом только
потому, что выделить подсписок, точнее даже его копию,
во Флэше мы можем с помощью отдельного метода.) Кроме
того, легко сцепить два маленьких
списка в один большой. Все только что
перечисленное можно делать, используя вместо
списка объект
Array.
Начнем с
удаления и вставки элементов. В объекте
Array для этого служит
метод splice, название
которого переводится как "соединять внахлест" -
например, магнитную ленту. (Есть, конечно, и другие
переводы, но этот - самый подходящий.) В этот метод
может передаваться произвольное количество аргументов.
Первый аргумент - индекс элемента, начиная с которого
надо производить удаление. Второй - количество удаляемых
элементов (это может быть ноль). Последующие аргументы -
это элементы, которые надо вставить на место удаленных.
Возвращает метод splice
субмассив удаленных элементов (а вовсе не "ничего", как
написано в онлайн-документации). Вот пример его работы:
следующий код
a_array = ["zero", "one",
"two", "three", "four"];
trace("Splice returns: " + a_array.splice(1, 3, 0,
1, 2));
trace("a_array: " + a_array);
trace("---------------");
trace("Splice returns: " + a_array.splice(1, 0, 0,
1, 2));
trace("a_array: " + a_array);
выводит в
консоль
Splice returns: one,two,three
a_array: zero,0,1,2,four
---------------
Splice returns:
a_array: zero,0,1,2,0,1,2,four
Отметим,
что если из массива ничего
не удаляется, то первый из вставленных элементов будет
иметь номер, указанный в первом аргументе метода
splice.
Следующий
метод, который мы рассмотрим - это
slice. Он принимает два
аргумента: индекс начала субмассива и индекс конца.
Причем, элемент, имеющий индекс конца, в возвращаемый
субмассив не включается. В отличие от
splice, метод
slice не изменяет исходный
массив. Иллюстрирующий все
это пример
a_array = ["zero", "one",
"two", "three", "four"];
trace("Slice returns: " + a_array.slice(2, 4));
trace("a_array = " + a_array);
trace("The biggest slice: " + a_array.slice(0,
a_array.length));
выводит в
консоль
Slice returns: two,three
a_array = zero,one,two,three,four
The biggest slice: zero,one,two,three,four
Обратите
внимание: последний вариант применения
slice - в вызове
a_array.slice(0, a_array.length)
- позволяет нам получить копию
массива.
Наконец,
рассмотрим метод concat.
Этот метод соединяет в один массив
произвольное количество объектов и других
массивов. Исходный
массив (у которого был
вызван метод concat) при
этом не изменяется; в результирующем
массиве его элементы идут
первыми, а затем уже следуют элементы
массивов (или единичные
объекты), которые были переданы в аргументах метода. Вот
пример работы метода concat:
a_array = ["zero", "one",
"two", "three", "four"];
b_array = a_array.concat(3, ["a", "b"], 10, [0, [1,
2], 3]);
trace("a_array = " + a_array);
trace("b_array = " + b_array);
trace("b_array[10] = " + b_array[10]);
На выходе
получаем:
a_array =
zero,one,two,three,four
b_array = zero,one,two,three,four,3,a,b,10,0,1,2,3
b_array[10] = 1,2
Обратите
внимание на последний вызов trace.
Мы проверили, что массив,
вложенный в другой, не "раскрывается", то есть не
разбивается на отдельные элементы при вызове
concat.
Эмуляция стека
С
эмуляцией стека ни малейших
проблем не предвидится, поскольку в классе
Array есть методы
push и
pop. Единственный вопрос,
который может возникнуть, - если мы захотим совместить
функциональность стека и
функциональность массива,
где нам искать элементы, помещенные в
массив при помощи
push? В начале или в конце
массива? Давайте проверим
это с помощью следующего кода:
electro_array = ["Резистор",
"Диод"];
trace(electro_array.push("Транзистор", "Тиристор",
"Дроссель"));
trace(electro_array);
trace(electro_array.pop());
trace(electro_array);
На выходе
получаем:
5
Резистор,Диод,Транзистор,Тиристор,Дроссель
Дроссель
Резистор,Диод,Транзистор,Тиристор
Итак,
элементы добавляются в конец
массива (причем можно добавлять сразу несколько
элементов), метод push
возвращает длину массива,
получившегося в результате.
Эмуляция очереди
Чтобы
эмулировать очередь, мы
должны научиться помещать элементы в
массив с одной стороны, а
вынимать их с другой. Поскольку мы уже умеем добавлять
элементы в конец массива,
нам нужно вынимать их спереди. Для этого существует
метод shift - он
аналогичен методу pop, но
действует с другого конца массива.
Впрочем, можно организовать
очередь и по-другому, вынимая элементы из конца
массива методом
pop. В таком случае нам
понадобится помещать элементы в начало
массива, и для этого служит
метод unshift. Таким
образом, мы можем использовать
массив не только как
очередь, но и как двунаправленную
очередь или, иначе говоря,
очередь с двусторонним
доступом (deque). Мы даже
можем устроить стек с
помощью shift и
unshift, хотя есть
подозрения, что он будет работать чуть медленнее
обычного. Вот примеры использования всех этих методов:
мы сначала двигаем очередь
в одну сторону, а потом в другую.
electro_array = ["Резистор",
"Диод"];
trace("electro_array: " + electro_array);
trace("push returns: " +
electro_array.push("Транзистор",
"Тиристор", "Дроссель"));
trace("electro_array: " + electro_array);
trace("shift returns: " + electro_array.shift());
trace("shift returns: " + electro_array.shift());
trace("electro_array: " + electro_array);
trace("unshift returns: " +
electro_array.unshift("Конденсатор",
"Микропроцессор"));
trace("electro_array: " + electro_array);
trace("pop returns: " + electro_array.pop());
trace("pop returns: " + electro_array.pop());
trace("electro_array: " + electro_array);
На выходе
имеем:
electro_array: Резистор,Диод
push returns: 5
electro_array:
Резистор,Диод,Транзистор,Тиристор,Дроссель
shift returns: Резистор
shift returns: Диод
electro_array: Транзистор,Тиристор,Дроссель
unshift returns: 5
electro_array:
Конденсатор,Микропроцессор,Транзистор,
Тиристор,Дроссель
pop returns: Дроссель
pop returns: Тиристор
electro_array: Конденсатор,Микропроцессор,Транзистор
Эмуляция хэш-таблицы
при помощи объекта
Мы уже
знаем, что можно обращаться к полям объекта удобным, но
недокументированным способом - при помощи квадратных
скобок. Нужно только иметь имя этого поля в виде
строки - чем не
хэш-таблица? Можно в
качестве имени даже передать число - оно будет
преобразовано в строку:
obj1 = {};
obj1["aaa"] = 1;
obj1["bbb"] = 2;
obj1[4] = 1000;
obj1[4.00001e-17] = 1000000;
obj1["!@#$%%^&*()_):"] = 222;
trace(obj1["bb" + "b"]);
trace(obj1[10*(4 + 0.00001)/1e18]);
trace(obj1["!@#$%%^&*()_):"]);
Запустив
это, на выходе получаем
2
1000000
222
А теперь
давайте скомандуем List Variables (Ctrl+Alt+V).
Результат:
Level #0:
Variable _level0.$version = "WIN 6,0,21,0"
Variable _level0.obj1 = [object #1, class 'Object']
{
aaa:1,
bbb:2,
4:1000,
4.00001e-17:1000000,
!@#$%%^&*()_)::222
}
Так что
любой объект является самой настоящей
хэш-таблицей. Причем ключи
могут быть какими угодно. Очевидно, что если
хэш-таблицей является любой
объект, а массив - это
объект, то массив тоже
можно использовать в качестве
хэш-таблицы. Однако что будет, если начать
вызывать специфические для массива
методы вроде push или
sort, а в
массиве имеются нечисловые
ключи? Сейчас разберемся.
Как Флэш трактует
"смешанный массив" (с числовыми и нечисловыми ключами)
Давайте
для разрешения этих вопросов сделаем такой "смешанный"
массив и произведем с ним
различные действия.
a_array = [23, 45, 67, 88];
a_array["kk"] = 345;
a_array[-1] = 3333333;
a_array.push(100);
trace("a_array: " + a_array);
trace("a_array.length: " + a_array.length);
// Выводим кусок побольше, чтобы посмотреть, не прячется ли
// что-то за дальней границей массива
trace("a_array.slice(2,6): " + a_array.slice(2,6));
trace("----- Names -----");
for(var name in a_array) trace(name);
trace("----------------");
trace(a_array["kk"]);
Вот что
выводит этот код:
a_array: 23,45,67,88,100
a_array.length: 5
a_array.slice(2,6): 67,88,100
----- Names -----
4
-1
kk
3
2
1
0
----------------
345
А вот что
мы получаем после команды List Variables
Level #0:
Variable _level0.$version = "WIN 6,0,21,0"
Variable _level0.a_array = [object #1, class
'Array'] [
0:23,
1:45,
2:67,
3:88,
4:100,
kk:345,
-1:3333333
]
Variable _level0.name = "0"
В
результате можно сделать следующие выводы.
Массив, как и обычный
объект, на деле представляет собой
хэш-таблицу. И с точки зрения того, как хранится
информация, все ключи в этой таблице равноправны.
Однако, когда мы начинаем с этой информацией работать, а
именно, пользоваться специфичными для
массива методами (например,
сортировкой), то
учитываются только ключи, являющиеся целыми
неотрицательными числами. И даже длина
массива (поле
length) корректируется
только с учетом таких ключей. Так что достать из
массива данные по ключу, не
являющемуся целым неотрицательным числом, можно лишь,
обратившись к ним напрямую. (Зато если ключ является
допустимым именем переменной, как, например,
kk в вышеприведенном коде,
то обращаться к хранящейся по этому ключу информации
можно и через точку: a_array.kk).
Строки
Работа со
строками - одна из наиболее
часто встречающихся задач при написании любой программы.
Мы уже говорили, что регулярных выражений во Флэше нет -
он не ориентирован на работу с текстами значительного
размера. Однако действия с обычными
строками производить
достаточно удобно.
Создание строк
(кавычки и new)
Мы уже
создавали строки в
примерах, встречавшихся ранее, а теперь давайте
упорядочим наши знания. Итак, во Флэше можно создать
строку двух разных видов:
объектного и примитивного. Вообще-то если
строкой пользоваться по
назначению, то разницу между этими двумя типами заметить
нельзя. Хотя, как мы далее узнаем, есть
(недокументированные) возможности расширить
функциональность объектных типов своими руками. Тогда
разница проявится. Как мы уже говорили, разница эта
состоит в том, что объект примитивного типа ведет себя
как объект read-only (только для чтения).
Изменить его, добавить в него поля и пр. мы не можем.
Во Флэше
есть несколько способов создать примитивную
строку. Первый и самый
распространенный - при помощи двойных или одинарных
кавычек. Два типа кавычек используется на случай, если
вам нужно вставить кавычки внутри
строки. То есть в качестве ограничителя
строки вы используете один
(любой) тип кавычек, а внутри самой
строки - другой. (Ту же
задачу можно решить, используя подходящую
escape-последовательность из приведенной ниже
таблицы).
Еще один
способ обзавестись примитивной
строкой - получить ее в результате выполнения
какого-либо метода, возвращающего
строки. Все такие методы возвращают именно
примитивные строки.
Таблица
4.1.
Escape-последовательность |
Символ |
\b |
Backspace
("забой") (ASCII 8) |
\f |
Form-feed
(знак смещения бланка) (ASCII 12) |
\n |
Line-feed
(перевод строки) (ASCII 10) |
\r |
Carriage
return (возврат каретки) (ASCII 13) |
\t |
Символ
табуляции (ASCII 9) |
\" |
Двойная
кавычка |
\' |
Одинарная
кавычка |
\\ |
Обратный слэш |
\000 - \377 |
Символ в
восьмеричном коде |
\x00 - \xFF |
Символ в
шестнадцатеричном коде |
\u0000 - \uFFFF |
16-битовый
символ Unicode (в шестнадцатеричном коде) |
Последний
способ получить примитивную строку
- это сделать ее из любого выражения функцией
String, служащей для
преобразования в строковый тип. Работает эта функция
достаточно очевидным образом. Для чисел возвращает
соответствующую строчку с десятичной записью числа
(возможно, с плавающей точкой). Для булевых выражений
выдает "true" или "false".
В случае undefined
возвращает пустую строку.
Наконец, в случае объектного типа вызывает метод
toString. По этим же
правилам производится преобразование в строчку и тогда,
когда оно совершается неявно (например, при передаче
аргумента строкового типа или в случае прибавления
какого-либо выражения к строке).
А вот
конструкция new String(выражение)
создает строку объектную, а
не примитивную. Выражение, которое передается в скобках,
в случае надобности конвертируется в
строку по правилам,
указанным в предыдущем абзаце.
Изредка
возникает проблема: нужно написать очень длинную
строку, которая не
помещается на экран. Перенос на следующую строчку можно
осуществить, закрыв кавычки и поставив знак
+. А можно - поставив
обратный слэш, как в С++. Правда, имеется проблема с
использованием последнего способа: слэши остаются в
строке (видимо, это просто
баг редактора). Но вычистить их оттуда довольно легко
(если у вас в строке не
будет других обратных слэшей). Так что иногда (при
копировании длинного текста из внешних источников)
бывает удобнее расставить в конце
строк обратные слэши, а уж потом их удалить
(например, способом, подробно описанным в конце этого
параграфа - заменой подстроки
"\" на пустую).
Что умеют строки во
Флэш
Сейчас мы
кратко опишем функциональность каждого из методов класса
String во Флэш. Все эти
методы работают как с объектными, так и с примитивными
строками. Это, кстати,
сразу означает, что ни один из методов не меняет
исходную строку (поскольку
невозможно изменить примитивный объект). Так что можно
писать, например, так: trace("my
name".toUpperCase()), и это действительно выведет
на печать MY NAME, хотя
подобный код и нельзя назвать хорошо читаемым.
Итак,
методы charAt и
charCodeAt позволяют
получить символ и его код по заданному индексу; метод
concat присоединяет к
нашей строке
строки, переданные в
аргументах, и возвращает результат; методы
indexOf и
lastIndexOf позволяют
находить подстроку в
строке, ведя поиск в прямом
и в обратном направлении соответственно (начиная с
заданного индекса); методы slice,
substr и
substring позволяют
разными способами выделять
подстроку; методы
toUpperCase и toLowerCase
переводят все буквы в заглавный или строчный регистр;
наконец, метод split
разбивает строку на лексемы
(по заданному разделителю). Особняком стоит метод
String.fromCharCode. Он
является чем-то вроде статического метода класса
String и не нуждается в
объекте строкового типа для своего вызова. Чтобы вызвать
этот метод, нужно так и написать:
String.fromCharCode, а в качестве аргументов
передать один или несколько кодов символов, из которых и
будет сформирована строка.
Конечно,
этого краткого описания недостаточно для уверенной
работы со строками Флэш,
так что сейчас мы приведем примеры применения всех этих
методов, кроме split.
Метод split настолько
хорош и полезен, что пример его применения мы выделим
отдельно и опишем в следующем подпараграфе. Все же
прочие примеры представлены в следующем коде:
a_str = "Очень длинная
строка";
trace(
"a_str.toLowerCase() = " +
a_str.toLowerCase()
); // Переводим строку в нижний регистр
trace(
"a_str.toUpperCase() = " +
a_str.toUpperCase()
); // Переводим строку в верхний регистр
trace("");
trace(
"a_str.charAt(3) = " +
a_str.charAt(3)
); // Буква "н"
trace(
"a_str.charCodeAt(5) = " +
a_str.charCodeAt(5)
); // Код пробела
trace("");
trace(
"a_str.concat(', да-да. ', a_str) = \n\t" +
a_str.concat(', да-да. ', a_str)
);
trace("");
trace(
"String.fromCharCode(46, 56, 74, 34, 45) = " +
String.fromCharCode(46, 56, 74, 34, 45)
);
trace("");
trace(
"a_str.indexOf('н') = " +
a_str.indexOf('н')
); // Первая буква "н"
trace(
"a_str.indexOf('нн') = " +
a_str.indexOf('нн')
); // Первое (оно же и последнее) сочетание букв
"нн"
trace(
"a_str.lastIndexOf('н') = " +
a_str.lastIndexOf('н')
); // Последняя буква "н"
trace(
"a_str.lastIndexOf('нн') = " +
a_str.lastIndexOf('нн')
); // Последнее (оно же первое) сочетание букв "нн"
trace(
"a_str.indexOf('н', 6) = " +
a_str.indexOf('н', 6)
); // Первая буква "н" после 6-й позиции (или на
6-й).
trace(
"a_str.lastIndexOf('н', 6) = " +
a_str.lastIndexOf('н', 6)
); // Последняя буква "н" перед 6-й позицией (или на
6-й).
trace("-------------");
/* Несколько примеров на метод slice */
/* Он работает так же, как аналогичный метод в массиве */
trace(
"a_str.slice(8, 12) = " +
a_str.slice(8, 12)
); // Кусок строки в районе букв "нн"
trace(
"a_str.slice(8, 3) = " +
a_str.slice(8, 3)
); // Если второй аргумент меньше первого, но
положителен,
// возвращается пустая строка.
trace(
"a_str.slice(8, -3) = " +
a_str.slice(8, -3)
); // Если второй аргумент отрицателен,
// отсчитываем от конца строки назад.
trace("-------------");
/* Посмотрим, чем от slice отличается substring */
trace(
"a_str.substring(8, 12) = " +
a_str.substring(8, 12)
); // Тот же результат, что и для slice
trace(
"a_str.substring(8, 3) = " +
a_str.substring(8, 3)
); // Если второй аргумент меньше первого,
// аргументы меняются местами
trace(
"a_str.substring(8, -3) = " +
a_str.substring(8, -3)
); // Если любой из аргументов отрицателен,
// он заменяется нулем. Затем, если нужно,
// аргументы меняются местами
trace("-------------");
/* Еще один метод выделения подстроки: substr */
trace(
"a_str.substr(8, 3) = " +
a_str.substr(8, 3)
); // Здесь второй аргумент задает длину подстроки.
trace(
"a_str.substr(8) = " +
a_str.substr(8)
); // Если же второй аргумент вовсе не передавать,
// то подстрока берется до конца строки
// substring и slice в этом случае работают точно так же.
Этот
длинный код выводит следующий длинный текст:
a_str.toLowerCase() = очень
длинная строка
a_str.toUpperCase() = ОЧЕНЬ ДЛИННАЯ СТРОКА
a_str.charAt(3) = н
a_str.charCodeAt(5) = 32
a_str.concat(', да-да. ', a_str) =
Очень длинная строка, да-да. Очень длинная строка
String.fromCharCode(46, 56, 74, 34, 45) = .8J"-
a_str.indexOf('н') = 3
a_str.indexOf('нн') = 9
a_str.lastIndexOf('н') = 10
a_str.lastIndexOf('нн') = 9
a_str.indexOf('н', 6) = 9
a_str.lastIndexOf('н', 6) = 3
-------------
a_str.slice(8, 12) = инна
a_str.slice(8, 3) =
a_str.slice(8, -3) = инная стр
-------------
a_str.substring(8, 12) = инна
a_str.substring(8, 3) = нь дл
a_str.substring(8, -3) = Очень дл
-------------
a_str.substr(8, 3) = инн
a_str.substr(8) = инная строка
Обратите
внимание на разницу в работе
slice, substring и
substr. Примеры
демонстрируют эту разницу для всех случаев, которые
могут быть важны на практике. Интересно также, что
существует (объявленная устаревшей) глобальная функция
substring, которой в
качестве первого аргумента передается
строка, а второй и третий
аргумент - числа. При этом работает она точно так же,
как метод substr класса
String (а вовсе не как
метод substring, как могло
бы показаться из-за совпадения названий)!
Нерассмотренным у нас пока остался простой, но
существенный вопрос: а как узнать длину
строки? Как и в случае
массива, специального
метода для этого не предусмотрено, зато можно обратиться
к свойству по имени length.
Пример работы со
строками и массивами
Согласитесь, что сделать простую замену
подстроки при помощи
стандартных методов строки
и массива - это то, что
программисты называют "good thing" (хорошее дело).
В первую очередь для этого нам понадобится метод
split, для данной
строки возвращающий
массив лексем, которые были
разделены в строке
переданным в аргументах метода разделителем. Давайте в
качестве разделителя возьмем удаляемую
подстроку. Затем мы можем
снова соединить этот массив
в строку методом
join, а в качестве "склеивающей"
строки передать ту строчку,
на которую мы хотим заменить удаляемую. Вот и все!
Смотрите, как это работает:
text_str = "Мы любим буги-вуги,
мы танцуем только буги";
new_str = text_str.split("уги").join("аги");
trace("text_str = " + text_str);
trace("new_str = " + new_str);
В
результате действительно получаем:
text_str = Мы любим буги-вуги,
мы танцуем только буги
new_str = Мы любим баги-ваги, мы танцуем только баги
|