Логические операторы && (AND) и || (OR). 1 страница
'&&' | - если левое выражение возвращает false, правое не выполняется. |
'||' | - если левое выражение возвращает true, правое не выполняется. |
Отличие от подобных операторов в С заключается в том, что в С возвращаемое значение либо 0, либо 1, тогда как в Perl возвращается результат выражения.
Оператор диапазона '..' Результат его работы зависит от контекста. В списковом контексте результат есть список с элементами, первый элемент которого это левое выражение и последнее - правое. Значение каждого элемента внутри списка увеличивается на 1. Данный оператор удобен для небольших циклов, т.к. память отводится для всего списка целиком. Поэтому будьте внимательны и не задавайте слишком большой диапазон.
for $i (1..4)
{ print "$i "; # Результат: 1 2 3 4
}
В скалярном контексте результат - логическое значение. Каждая '..' операция устанавливает свое собственное состояние. Это false до тех пор, пока левый операнд false. Как только он стал true, результат - true до тех пока правый true, после чего опять - false. Если вы не хотите проверять правый операнд, то используйте оператор '...'.
Правый операнд не вычисляется, пока результат false и левый операнд не вычисляется, пока результат true. Приоритетность оператора '..' немного ниже чем '&&' и '||'. Возвращаемое значение если flase - нулевая строка, если true - порядковый номер, начиная с 1. Порядковый номер обнуляется для каждого нового диапазона.
@abc = ('a'..'z'); # Массив малых букв латинского алфавита
@digits = (0..9); # Массив цифр
Условный оператор '?:' Этот оператор работает так же как и в С. Если выражение перед '?' истинно, то выполняется аргумент перед ':' - иначе после ':'.
$i = 1;
$i > 1 ? print "больше" : print "меньше"; #Результат: меньше
Операторы присваивания. '=' - обычный оператор "присвоить" правое значение переменной слева. Вся эта группа операторов подобна операторам С, т.е.
$i += 2; #эквивалентно $i = $i + 2;
Остальные операторы этой группы работают аналогично. Допустимы следующие операторы:
**=, +=, -=, .=, *=, /=, %=, x=, &=, |=, ^=, <<=, >>=, &&=, ||=
Приоритет всей этой группы операторов равен приоритету '='.
Оператор ',' (запятая) В скалярном контексте выполняется левый аргумент, результат игнорируется, затем правый, и его результат есть результат действия оператора. В списковом контексте это разделитель элементов списка, включает указанные элементы в список.
Операторы not, and, or, xor Оператор логическое not (отрицание). - унарный not возвращает противоположное значение, полученное выражением справа. Он эквивалентен '!'.
Оператор логическое and (И). Выполняет логическую конъюнкцию двух выражений. Эквивалентен '&&', но имеет очень низкий приоритет и "краткость" действия, т. е. если левое выражение равно false - правое не выполняется.
Логическое or (ИЛИ). Выполняет логическую дизъюнкцию двух выражений. Эквивалентен '||', но имеет очень низкий приоритет и "краткость" действия, т. е. если левое выражение равно true - левое не выполняется.
Логическое xor (исключающее ИЛИ). Выполняет логическое исключающие или. Всегда выполняются оба правое и левое выражение.
В Perl отсутствуют операторы языка С, такие как:
унарное & | - получить адрес. Для этого применяется '\'. |
унарный * | - переадресация. |
(TYPE) | - совмещение типов. |
Операторы ограничители строк. Обычно ограничителями строк мы считаем литералы, но в Perl это операторы, выполняющие разного рода интерполяцию и поиск по шаблону. Вы можете сами задавать удобные для вас ограничители. В следующей таблице приведен полный перечень вариантов. Фигурные скобки '{}' обозначают любой символ, используемый для ограничителя. В случае использования скобок (круглых '( )', квадратных '[ ]', фигурных '{ }', угловых '< >') в начале ставится открывающаяся скобка, а в конце закрывающая.
По умолчанию | Полное | Функция | Интерполяция |
'' | q{} | Literal | нет |
"" | qq{} | Литерал | да |
`` | qx{} | Команда | да |
qw{} | Список слов | нет | |
// | m{} | Шаблон | да |
s{}{} | Подстановка | да | |
tr{}{} | Трансляция | нет |
В строках, допускающих интерполяцию, имена переменных, начинающиеся с символов '$' или '@' - интерполируются, т.е. в строку вставляется значение строки или массива. Данные последовательности символов имеют специальное значение:
\t | символ табуляции |
\n | символ новой строки |
\r | возврат |
\f | перевод формата |
\v | вертикальная табуляция |
\b | backspace (забой) |
\a | звонок |
\e | escape |
\034 | восьмеричный символ |
\x1a | шестнадцатеричный символ |
\c[ | символ управления |
\l | нижний регистр следующего символа |
\u | верхний регистр следующего символа |
\L | нижний регистр для всех символов до \E |
\U | верхний регистр для всех символов до \E |
\E | ограничитель смены регистра |
\Q | отмена действия метасимволов до \E |
Шаблоны интерполируются как регулярные выражения. Это выполняется вторым проходом после интерполяции переменных, поэтому в шаблоны можно вставлять переменные. Для отмены интерполяции используйте '\Q'. Если вы применяете вложенные ограничители, то внутренние ограничители работать не будут.
?PATERN? Действие этого оператора аналогично /шаблон/, но выполняется до первого совпадения. Это удобно для поиска наличия какой-нибудь строки в одном или множестве файлов. Это не очень удачный оператор.
m/PATERN/gimosx, /PATERN/gimosx Поиск в строке по патерну (шаблону). В скалярном контексте возвращает логическое значение true (1) или false (''). Если строка не указана с помощью операторов '=~' или '!~', поиск ведется в строке $_. Опции:
g | - Глобальный поиск. Поиск всех вхождений. |
i | - Сравнение не зависит от регистра (верхний или нижний) |
m | - Строка многострочная. |
o | - однопроходная компиляция |
s | - однострочная строка |
x | - используются расширенные регулярные выражения. |
Если '/' - ограничитель, то начальное 'm' можно опустить. С помощью него в качестве ограничителя может быть любой символ кроме пробела.
PATTERN может содержать переменные, которые будут интерполироваться (перекомпилироваться) каждый раз в момент вычисления. Переменные $) и $| не интерполируются. Если вы хотите, что бы такой шаблон интерполировался один раз - добавьте /o. Это необходимо делать в циклах поиска для увеличения быстродействия. Если PATERN - нулевая строка, то используется последнее регулярное выражение.
В скалярном контексте возвращается список, элементы которого - результаты выполнения выражений в скобках патерна ($1, $2, $3...). Обратите внимание, что первый элемент $1.
$a = "/usr/local/perl/perl.bin"; # Анализируемая строка
Цель: Создать массив @dirs с именами директорий.
Решение: Самый простой способ - воспользоваться split('\/'), но мы используем скобки.
@dirs =($a=~ m[/(\w*)/(\w*)/(\w*)/(\w*)]);
Здесь 'm[' - использовать квадратные скобки как ограничители, (\w*)- шаблон алфавитно-цифровой последовательности.
В результате @dirs равен ('usr', 'local', 'perl')
q/строка/, 'строка' Строка литералов. Не интерполируется. Внутри строки разрешается использовать \' или \\ для обозначения символов ' и \ :
print q#Привет.#; # Результат Привет.
print 'O\'K'; # O'K
qq/строка/, "строка" Интерполируемая строка:
$var = 13;
print "\$var = $var"; # Результат: $var = 13
qx/строка/, `строка` Строка интерполируется, а потом выполняется как системная команда:
print `date`; #Результат: Thu Nov 14 13:36:49 MSK 1996
qw/строка/ Возвращает список, элементы которого - слова строки, разделенные пробелами:
print qw/Построимся и спасемся!/; # ('Построимся','и','спасемся!')
s/шаблон/подстрока/egimosx Поиск по шаблону и в случае успеха замена подстрокой. Возвращает количество произведенных подстановок, иначе false (0). Если строка в которой ведется поиск не указана (операторы =~ или != ), то используется переменная $_ . Если в качестве разделителя '/' использовать одинарную кавычку ('), то интерполяции не будет, иначе можно применять переменные в шаблоне или подстроке. Опции:
e | - Рассматривать правую часть как выражение. |
g | - Глобальный поиск. |
i | - Без различия регистра букв |
m | - многострочная переменная |
o | - компилировать шаблон один раз |
s | - однострочная переменная |
x | - расширенное регулярное выражение |
Разделитель '/' можно заменить на любой алфавитно-цифровой символ кроме пробела:
Листинг 6-3.Регулярные выражения.
$var = "12345"; # исходная строка
$var =~ s/1/0/; print $var; # Заменить '1' на '0'. Результат 02345
$var =~ s(5)(.); print $var; # Заменить '5' на '.' Результат 0234.
Здесь в качестве разделителя применены скобки, поэтому подстрока взята в две скобки.
$var =~ s/\d*/каламбур/; Заменить все цифры. Результат 'каламбур.'
$var =~ s/а/о/g; print $var; # Заменить все 'а' на 'о'.
$var = "12 34"; # Новое значение
$var =~s/(\d\d)\s(\d\d)/$2 $1/; print $var;#Поменять местами числа
tr/таблица1/таблица2/cds, y/таблица1/таблица2/cds Замена всех символов из "таблица1" на соответствующий символ из "таблица2". Результат - количество замен или стираний. Без оператора =~ или != операция выполняется со строкой $_. Для совместимости с программой sed вместо tr можно писать 'y'. Опции:
c | - дополнение "таблица1" |
d | - стереть найденные, но не замененные символы. |
s | - "сжать" повторяющиеся замененные символы. |
Если указана опция /d, таблица2 всегда интерпретируется как положено: если таблица2 короче, чем таблица1, то символ из таблицы1 интерпретируется всегда. Если таблица2 - null, то все символы строки остаются неизменными. Это удобно для подсчета количества символов в строке определенного класса или сжатия повторяющихся символов:
Листинг 5.4.Регулярные выражения.
$s = "hello"; # Исходная строка
$s =~ tr/a-z/A-Z/; # Заменить малые буквы на большие.
print $s; # Результат 'HELLO'
$s = 'Hel....lo';
$s =~ tr/a-zA-z/_/c; # Заменить все не буквы на '_'
print $s; # Результат 'Hel____lo'
$s =~ tr/_/ /s; # Заменить '_' на ' ' и сжать.
print $s; # Результат 'Hel lo'
$s =~ tr/a-zA-Z /a-zA-Z/d; # Удалить все не буквы.
print $s; # Результат 'Hello'
Если один символ несколько раз указан в таблице1, то применяется только первая замена.
Операторы ввода-вывода. В Perl существует несколько операторов ввода-вывода. Первый это скобки из символа '`' - акцента. Строка в этих скобках воспринимается, как системная команда и результат ее действия возвращается как "псевдо" литерал. В скалярном контексте это строка, содержащая весь результат, а в списковом - список, элементы которого - строки результата. Статус выполненной команды хранится в переменной $?.
Следующая команда ввода вывода выглядит как '<файл>'. Вычисление <файл> приводит к чтению строки из файла. Обратите внимание, что 'файл' здесь не имя файла, а указатель файла, который создается функцией open(). В скалярном контексте читается одна строка вместе с символом '\n' - перевода строки, а в списковом - весь файл читается в список, элементы которого суть строки файла. В случае обнаружения конца файла результат оператора не определен и воспринимается как false. Если не указана переменная результата, то по умолчанию это $_. Указатель файла по умолчанию STDIN - стандартный ввод:
while(<>) { print; }; # Прочитать и вывести весь файл STDIN
У оператора '<>' есть одна отличительная особенность. Если в командной строке нет никаких аргументов, то читается стандартный ввод, если есть аргументы, то они считаются именами файлов, которые последовательно читаются.
Если в угловых скобках записана переменная, то содержимое этой переменной считается именем указателя файла или ссылкой на указатель файла. Если такого указателя не существует, то содержимое переменной воспринимается как шаблон имен файлов и результат - имена файлов на диске, подходящих по шаблону:
while(<*.pl>) { print;}; # То же что и ls *.pl
@files = <*>; # Массив @files содержит имена файлов в директории
но лучше сделать: @files = glob("*"); т.к. внутри скобок можно использовать переменные.
Слияние констант. Как и С Perl выполняет возможные вычисления в период компиляции. Так подстановка символов после '\', операция конкатенации строк, арифметические выражения, содержащие только одни константы, все это делается в момент компиляции, что существенно увеличивает скорость выполнения программы.
Целочисленная арифметика. По умолчанию Perl выполняет арифметику с плавающей запятой, но если вы укажете:
use integer;
то компилятор будет использовать целочисленную арифметику до конца текущего блока, хотя вложенный блок может это и отменить в своих пределах с помощью:
no integer;
Встроенные функции.
Встроенные функции используются как термы выражений и подразделяются на две категории: списковые операторы и унарные операторы. Это влияет на их приоритет по отношению к оператору ',' - запятая. Списковые операторы могут иметь множество (список) аргументов, а унарные только один. Таким образом, запятая завершает аргументы унарного оператора и разделяет аргументы спискового. Аргумент унарного оператора воспринимается обычно в скалярном контексте, а спискового - как в скалярном, так и списковом, причем скалярные аргументы идут первыми. Аргументы функций можно заключать в круглые скобки и таким образом обозначать, что "это функция" и приоритет не имеет значения, иначе это списковый или унарный оператор с определенным фиксированным приоритетом. Пробел после имени функции и скобкой значения не имеет:
print 1 + 2 + 3; # результат 6
print(1+2)+3; # результат 3
print (1+2)+3; # опять 3
print (1+2+3); # 6
Если функция возвращает результат, как в скалярном, так и в списковом контексте, то код выхода по ошибке - скаляр с неопределенным значением или пустой список.
Запомните правило: Не существует общего правила преобразования списка в скаляр!
Перечень встроенных функций (без описания системных вызовов) приведен в приложении.
Подпрограммы.
Для применения подпрограммы ее необходимо определить либо в текущем модуле (файле), либо во внешнем модуле (файле). Подпрограммы определяются и декларируются следующим образом:
· sub имя; - Только декларация. Определение ниже.
· sub имя (прототипы); - То же но с декларацией параметров.
· sub имя блок; - Декларация и определение.
· sub имя (прототипы) блок; - То же, но с параметрами.
Для определения динамической анонимной подпрограммы можно указать:
· $переменная = sub блок;
Для импортирования подпрограмм из других модулей используйте:
· use модуль qw(подпрограмма1 подпрограмма2 );
Вызов подпрограммы:
имя(список параметров); # символ '&' можно не указывать.
имя список; # Если подпрограмма уже декларирована.
&имя; # Параметры в @_
Все параметры передаются подпрограмме как массив @_. Соответственно $_[0] - первый параметр, $_[1] - второй и т.д. Массив @_ - локальный, но он содержит адреса параметров, поэтому можно изменять значение параметров. Возвращаемое значение подпрограммы - результат последнего оператора. Это может быть как скаляр, так и массив. Можно принудительно возвращать результат, используя функцию return().
Подпрограмму можно вызвать, используя префикс '&' перед именем подпрограммы. Если подпрограмма предварительно продекларирована, то префикс и скобки можно опустить.
Private переменные. Для применения переменных доступных только внутри блока или подпрограммы необходимо определить их с помощью функции my(список). Если переменная одна, то скобки можно опустить. my() декларирует private переменные в пределах текущей подпрограммы, блока, функции eval() или do/require/use файлов. Private переменные аналогичны auto переменным в С:
Листинг 5.5.Подпрограммы.
# Программа вычисления факториала.
print fact(3); # вычислить факториал 3*2*1
sub fact # Определяем подпрограмму.
{ my $m; # private переменная но не local !
$m = $_[0];
return 1 if $m <= 1;
return($m * fact($m -1));
}
Можно указывать начальные значения private переменных как:
· my(список) = выражение;
Так для вышеприведенного примера лучше было написать:
· my($m) = $_[0];
Переменные типа local. В общем лучше использовать private переменные, т. к. это надежней и быстрее. private переменные обеспечивают лексическую область применения (видимости), а local - динамическую. Обычно это переменные форматов, значение которых должно быть видимо из вызываемых подпрограмм. Применение функции local() нецелесообразно в циклах, так как она вызывается каждый раз и таким образом заметно увеличивает время выполнения цикла.
Прототипы (prototypes). Для краткого описания типа передаваемых подпрограмме параметров можно применять прототипы. В Perl существуют следующие прототипы:
Декларация | Пример вызова |
sub mylink($$) | mylink $old, $new |
sub myvec($$$) | myvec $var, $offset, 1 |
sub myindex($$;$) | myindex &getstring, "substr" |
sub myreverse(@) | myreverse $a, $b, $c |
sub myjoin($@) | myjoin ":",$a,$b,$c |
sub mypop(\@) | mypop @array |
sub mysplice(\@$$@) | mysplice @array, @array, 0, @pushme |
sub mykeys(\%) | mykeys %{$hashref} |
sub myopen(*;$) | myopen HANDLE, $name |
sub mypipe(**) | mypipe READHANDLE, WRITEHANDLE |
sub mygrep(&@) | mygrep { /foo/ } $a, $b, $c |
sub myrand($) | myrand 42 |
sub mytime() | mytime |
Здесь:
· \'символ' - параметр с типом 'символ'
· '@' или '%' - все оставшиеся параметры как список
· '$' - скаляр
· '&' - безымянная подпрограмма
· '*' - ссылка на таблицу имен
· ';' - разграничитель обязательных и не обязательных параметров.
Ссылка как параметр. Иногда нужно в качестве параметра передать подпрограмме не значение элемента массива, а ссылку на него, чтобы подпрограмма могла изменить значение элемента. Для этого в Perl к имени переменной добавляется символ '*' Подобное выражение называют 'type glob' так же как в Unix символом '*' обозначают "все возможные значения". Поэтому '*' для массива означает "все элементы массива". Для скаляров употреблять '*' не имеет смысла, т.к. они и так передаются ссылкой и вы можете изменять значение параметра, изменяя, например, переменную $_[0].
Переопределение встроенных функций. Большинство встроенных функций Perl можно переопределить своими собственными. Обычно это делают для удобства совместимости Perl для разных платформ систем. Для этого нужно перечислить имена этих функций в виде:
· use subs 'функция1', 'функция2' ....;
и далее в модуле определить сами функции.
Автозагрузка. Если вы попытаетесь вызвать несуществующую функцию, то Perl выдаст сообщение об ошибке. Но если вы определите подпрограмму с именем 'AUTOLOAD', то она будет вызвана с теми же параметрами, а переменная $AUTOLOAD будет содержать имя несуществующей подпрограммы. Данный механизм очень удобен для средств отладки.
Модули (packages).
В Perl реализован механизм модулей. Модуль это группа подпрограмм и переменных, обычно включенных в один файл. Внутри одного модуля можно определить другой модуль. Начало модуля определяется директивой:
· packages имя_модуля;
Конец модуля это конец блока или файла. Головной модуль имеет по умолчанию имя main. На имя внутри модуля можно ссылаться, добавляя '::' после имени модуля:
$main::var1 - переменная в головном модуле.
::var1 - то же самое. Имя main можно опускать.
$модуль1::var1 - переменная в модуле 'модуль1'
$модуль1::модуль2::var1 - Модуль2 содержится в модуле 1.
Только идентификаторы, начинающиеся с буквы или символа '_', хранятся в пространстве имен текущего модуля. Остальные хранятся в пространстве головного модуля main. Кроме этого имена STDIN, STDOUT, STDERR, ARGV, ARGVOUT, ENV, INC и SIG так же хранятся в головном модуле.
Таблицы имен. Все имена модуля хранятся в ассоциативном массиве (хеше) с именем модуля, к которому добавлены символы "::". Таким образом, имена головного модуля хранятся в %main:: , модуля 'mod1' в %mod1:: и т.д. Выражение вида *имя указывает значение элемента хеша 'имя', это удобно для определения констант.
*pi = \3.14159;
Здесь переменная $pi - это константа пи, которую уже нельзя изменить.
Конструкторы и деструкторы. Конструктор - это подпрограмма, которая выполняется в момент создания объекта, а деструктор - удаления объекта. Для модуля это подпрограммы с именами BEGIN и END. При определении этих подпрограмм слово sub можно опускать.
Конструктор BEGIN выполняется сразу, как только возможно, т.е. как только он определен, даже не завершая дальнейший разбор программы. Можно указать несколько блоков BEGIN. Они будут выполняться один за другим в порядке определения.
Деструктор END выполняется последним как только возможно, т.е. при завершении работы интерпретатора. Можно указать несколько блоков END, при этом они будут выполняться в обратном определению порядке.
Классы. В Perl нет специального синтаксиса для классов. Но функционально полноценными классами могут быть модули. При этом подпрограммы модуля становятся методами, а с помощью массива @ISA можно реализовать механизм наследования в классах. Более подробно классы рассматривать не будем.
Создание библиотеки. Если вы хотите создать модуль отдельным файлом и использовать как библиотеку подпрограмм, при этом вызывать подпрограммы библиотеки, не указывая имени модуля, вам необходимо оформить модуль следующим образом:
package имя_модуля; #Такое же, как имя файла без расширения '.pm'
require Exporter; # Обязательная строка для экспорта имен
@ISA = qw(Exporter); # -//-
@EXPORT = qw(func1 func2) #Перечисляем имена функций. Нет запятой!
@EXPORT_OK = qw($переменная @массив); #Указать публичные #переменные, массивы и т.д. если необходимо
{ # Начало блока модуля
.....
sub func1
........
sub func2
........
1;
}
Данный файл с расширением ".pm" должен храниться в одной из библиотечных директорий Perl. Они перечислены в массиве @INC, одна из них обычно "/usr/local/lib/perl/".
В головной программе вы указываете:
· use имя_модуля;
и вам становятся доступны имена подпрограмм данного модуля.
Perl библиотеки. Стандартный набор библиотек обычно поставляется с дистрибутивом Perl, они разделяются на pragma библиотеки (работают как директивы компилятору) и стандартные библиотеки.
Pragma библиотеки. Данные библиотеки используют как:
· use имя;
когда хотят включить действие и
· no имя;
когда хотят выключить.
В стандартный набор входят следующие pragma:
diagnostics- Включить режим расширенной диагностики.
integer- Использовать целочисленную арифметику.
less- Режим минимальной загрузки компилятора.
overload- Режим переопределения операторов.
sigtrap- Режим слежения за прерываниями.
strict- Режим ограниченного использования "опасных" операторов.
subs- Режим обязательного декларирования подпрограмм.
Стандартные библиотеки. С точки зрения web-программирования наибольший интерес представляют библиотеки для работы с тегами HTML и для доступа к базам данных. Их мы рассмотрим чуть позднее и более подробно. Подробное описание каждой библиотеки записано в самом файле.
CPAN. Программисты всего мира, работающие с Perl, создали общедоступную библиотеку модулей CPAN . Она доступна через Интернет и содержит огромное количество различных по назначению модулей. К ним относятся документаторы, системные интерфейсы, интерфейсы работы с базами данных, работа в сети, с файлами, Интернет-броузеры, системы поиска, огромное количество CGI скриптов для Web серверов и многое-многое другое.
Форматы.В Perl реализован удобный метод создания форматированных отчетов. С помощью оператора format вы описываете заголовки, размеры полей, указываете положение данных на листе в удобной текстовой форме. Затем выполняете команду write(файл), которая выводит отформатированные данные в указанный файл.
Оператор format имеет следующий синтаксис:
format имя =
FORMLIST
.
Обратите внимание на то, что описание формата идет после строки format и заканчивается символом '.' в начале строки. Здесь 'имя' - это имя формата, такое же как и имя указателя выходного файла. Если 'имя' отсутствует то значение по умолчанию - STDOUT.
FORMLIST - это строки формата. Они бывают трех типов:
1. Комментарий. Строка начинается символом '#'.
2. Описатель полей данных (picture).
3. Строка аргументов используемых описателем.
Описатель - это строка, которая выводится в виде "как есть" за исключением специально обозначенных форматов полей данных. Каждое поле начинается либо символом '@', либо '^'. В описательной строке указывается только положение и вид выводимых данных, но не имена полей и переменных. Для этого предназначена следующая строка аргументов которая следует всегда после описателя и содержит имена переменных или целые выражения в порядке указанном описателем. Размер и вид поля в описателе обозначается символами:
"<<<<" | - выравнить значение по правому краю. |
">>>>" | - -//- по левому. |
"|" | - -//- по центру. |
"####.###" | - формат числа с точкой. |
"@*" | - многострочная строка. Данные выводятся в колонку. |
Размер поля равен количеству указанных символов. Символ '^' в начале поля имеет специальное значение. Так:
· "^####" - пусто если переменная не определена.
для строчного скаляра:
· "^<<<<<" - Выводится сколько возможно символов, а значение переменной меняется на остаток, вывод которого можно продолжить на следующих строках, которые могут иметь свои поля.
Дата добавления: 2015-08-26; просмотров: 1246;