Выражения 2 страница
Лекция № 5
Тема: Управляющие конструкции Turbo Pascal
Цель: Ознакомить студентов с управляющими конструкциями языка Turbo Pascal. Дать понятия о операторах цикла с известным числом повторов, с предусловием, с постусловиям, с оператором выбора и оператором безусловного перехода. Научить применять при решении задач.
План:
5.1 Условный оператор.
5.2 Оператор повторений.
5.3 Операторы цикла.
5.4 Оператор выбора.
5.5 Метки и операторы перехода.
5.1 Условный оператор.
Условный оператор позволяет проверить некоторое условие и в зависимости от результатов проверки выполнить то или иное действие. Таким образом, условный оператор - это средство ветвления вычислительного процесса.
Структура условного оператора имеет следующий вид:
IF <условие> THEN <оператор1> ELSE <оператор2>,
где IF, THEN, ELSE - зарезервированные слова (если, то, иначе); <условие> - произвольное выражение логического типа; <оператор1>, <оператор2> - любые операторы языка Турбо Паскаль.
Условный оператор работает по следующему алгоритму. Вначале вычисляется условное выражение <условие>. Если результат есть TRUE (истина), то выполняется <оператор1>, а <оператор2> пропускается; если результат есть FALSE (ложь), наоборот, <оператор1> пропускается, а выполняется <оператор2>. Например:
var
х, у, max: Integer;
begin
.......
if x > max then
у := max else
y := x;
При выполнении этого фрагмента переменная Y получит значение переменной X, если только это значение не превышает МАХ, в противном случае Y станет равно МАХ.
Часть ELSE <оператор2> условного оператора может быть опущена. Тогда при значении TRUE условного выражения выполняется <оператор1>, в противном случае этот оператор пропускается:
var
х, у, max: Integer;
begin
...
if x > max then max := x;
Y := x;
...
end.
В этом примере переменная Y всегда будет иметь значение переменной X,а в МАХ запоминается максимальное значение X.
Поскольку любой из операторов <оператор1> и <оператор2> может быть любого типа, в том числе и условным, а в то же время не каждый из «вложенных» условных операторов может иметь часть ELSE <оператор2>, то возникает неоднозначность трактовки условий. Эта неоднозначность в Турбо Паскале решается следующим образом: любая встретившаяся часть ELSE соответствует ближайшей к ней «сверху» части THEN условного оператора. Например:
var
a,b,c,d:Integer;
begin
a:=1; b:=2; c:=3; d:=4;
if a > b then
if с < d then
if с < 0 then
с := 0
else
a:= b; {а равно 1}
if a > b then
if с then
if с then
с:= 0
else
else
else
a:= b; {а равно 2}
end.
Рассмотрим программу (пример 5.1), которая вводит произвольное десятичное целое число в диапазоне 0...15, преобразует его к шестнадцатеричному и выводит на экран полученный результат.
Пример 5.1
Program Hex;
{Программа вводит с клавиатуры целое число в диапазоне от 0 до 15, преобразует его к шестнадцатеричной системе счисления и выводит результат на экран}
var
n : Integer; {Вводимое число}
ch : Char; {Результат}
begin
Write ('n = ') ;
ReadLn(n); { Вводим число }
{Проверяем число на принадлежность к диапазону 0...15}
if (n >= 0) and (n <= 15) then
begin {Да, принадлежит диапазону}
if n < 10 then
ch:=chr(ord('0') + n)
else
ch:=chr(ord('A') + n- 10);
WriteLn('n = ',ch)
end
else {He принадлежит диапазону}
WriteLn('Ошибка')
end.
В шестнадцатеричной системе счисления используется 16 цифр в каждом разряде: цифры 0...9 обозначают первые 10 возможных значений разряда, буквы A...F - остальные шесть.
В программе учитывается непрерывность и упорядоченность множеств цифр 0...9, букв A...F и их кодов
5.2 Операторы повторений
В языке Турбо Паскаль имеются три различных оператора, с помощью которых можно запрограммировать повторяющиеся фрагменты программ.
Счетный оператор цикла FOR имеет такую структуру:
FOR <пар_цик> := <нач_знач> ТО <кон_знач> DO <оператор>.
Здесь FOR, TO, DO - зарезервированные слова (для, до, выполнить);
<пар_цик> - параметр цикла - переменная типа INTEGER (точнее, любого порядкового типа);
<нач_знач> - начальное значение - выражение того же типа;
<кон_знач> - конечное значение - выражение того же типа;
<оператор> - произвольный оператор Турбо Паскаля.
При выполнении оператора FOR вначале вычисляется выражение <нач_знач> и осуществляется присваивание <пар_цик> : = <нач_знач>. После этого циклически повторяется:
проверка условия <пар_цик> <= <кон_знач>; если условие не выполнено, оператор FOR завершает свою работу;
выполнение оператора <оператор>;
наращивание переменной <пар_цик> на единицу.
В качестве иллюстрации применения оператора FOR рассмотрим программу, осуществляющую ввод с клавиатуры произвольного целого числа N и вычисление суммы всех целых чисел от 1 до N (пример 5.2).
Пример 5.2
Program Summ_of_Integer;
{Программа вводит целое положительное число N и подсчитывает сумму всех целых чисел от 1 до N}
var
i, n, s : Integer;
begin
Write('N = ');
ReadLn(n); . {Вводим N}
s := 0; {Начальное значение суммы}
for i : = 1 to n do {Цикл подсчета суммы}
s : = s + i;
writeln('Сумма = ',s) {Выводим результат}
end.
Отметим два обстоятельства. Во-первых, условие, управляющее работой оператора FOR, проверяется перед выполнением оператора <оператор>: если условие не выполняется в самом начале работы оператора FOR, исполняемый оператор не будет выполнен ни разу. Другое обстоятельство - шаг наращивания параметра цикла строго постоянен и равен (+1). Существует другая форма оператора:
FOR<пар_цик>: = <нач_знач> DOWNTO <кон_знач> DO <оператор>
Замена зарезервированного слова ТО на DOWNTO означает, что шаг наращивания параметра цикла равен (-1), а управляющее условие приобретает вид <пар_цик> = <кон_знач>.
Пример 5.2 можно модифицировать так, чтобы сделать его пригодным для подсчета любых сумм - положительных и отрицательных:
...
s := 0;
if n >= 0 then
for i := 1 to n do
s := s + i else
for i := -1 downto n do s : = s + i ;
...
Два других оператора повторений лишь проверяют условие выполнения или повторения цикла, но не связаны с изменением счетчика цикла.
5.3 Операторы цикла.
Оператор цикла WHILE с предпроверкой условия:
WHILE <условие> DO <оператор>.
Здесь WHILE, DO - зарезервированные слова (пока [выполняется условие], делать);
<условие> - выражение логического типа;
<оператор> - произвольный оператор Турбо Паскаля.
Если выражение <условие> имеет значение TRUE, то выполняется <оператор>, после чего вычисление выражения <условие> и его проверка повторяются. Если <условие> имеет значение FALSE , оператор WHILE прекращает свою работу.
Рассмотрим пример 5.3, иллюстрирующий использование оператора WHILE. Найдем так называемое «машинное эпсилон» - такое минимальное, не равное нулю вещественное число, которое после прибавления его к 1.0 еще дает результат, отличный от 1.0.
Пример 5.3
Program EpsilpnDetect;
{Программа вычисляет и выводит на экран значение "машинного эпсилон"}
var
epsilon: Real;
begin
epsilon := 1;
while epsilon/2 + 1 > 1 do
epsilon := epsilon/2
WriteLn('Машинное эпсилон = ',epsilon)
end.
У читателя, привыкшего к непрерывной вещественной арифметике, может вызвать недоумение утверждение о том, что в дискретной машинной арифметике всегда существуют такие числа 0<X<eps, что 1.0+Х=1.0. Дело в том, что внутреннее представление типа REAL может дать «лишь» приблизительно 1014 возможных комбинаций значащих разрядов в отведенных для него 6 байтах. Конечно же, это очень большое число, но оно несопоставимо с бесконечным множеством вещественных чисел. Аппроксимация бесконечного непрерывного множества вещественных чисел конечным (пусть даже и очень большим) множеством их внутреннего машинного представления и приводит к появлению «машинного эпсилон».
Оператор цикла REPEAT... UNTIL с постпроверкой условия:
REPEAT <тело_цикла> UNTIL <условие>.
Здесь REPEAT, UNTIL- зарезервированные слова (повторять до тех пор, пока не будет выполнено условие);
<тело_цикла> - произвольная последовательность операторов Турбо Паскаля;
<условие> - выражение логического типа.
Операторы <тело_цикла> выполняются хотя бы один раз, после чего вычисляется выражение <условие>: если его значение есть FALSE, операторы <тело_цикла> повторяются, в противном случае оператор REPEAT. . . UNTIL завершает свою работу.
Для иллюстрации применения оператора REPEAT... UNTIL модифицируем программу из примера 2.3. Модификация (пример 2.7) состоит в том, что программа будет все время повторять цикл ввода символа и печати его кода до тех пор, пока очередным символом не будет символ CR (вводится клавишей Enter).
Пример 5.4
Program Codes_of_Chars;
{Программа вводит символ и выводит на экран его код. Для завершения работы программы нужно дважды нажать Enter}
var
ch : Char; {Вводимый символ}
const
CR = 13; {Код символа CR}
begin
repeat
ReadLn(ch);
WriteLn(ch,' = ',ord(ch))
until ord(ch) = CR
end.
Обратите внимание: пара REPEAT... UNTIL подобна операторным скобкам begin. .. end, поэтому перед UNTIL ставить точку с запятой необязательно.
Для гибкого управления циклическими операторами FOR, WHILE и REPEAT в состав Турбо Паскаля включены две процедуры:
BREAK - реализует немедленный выход из цикла; действие процедуры заключается в передаче управления оператору, стоящему сразу за концом циклического оператора;
CONTINUE - обеспечивает досрочное завершение очередного прохода цикла; эквивалент передачи управления в самый конец циклического оператора.
5.4 Оператор выбора.
Оператор выбора позволяет выбрать одно из нескольких возможных продолжений программы. Параметром, по которому осуществляется выбор, служит ключ выбора -выражение любого порядкового типа (любого из рассмотренных, кроме типов REAL и STRING).
Структура оператора выбора такова:
CASE <ключ_выбора> OF <список_выбора> [ELSE <операторы>] END
Здесь CASE, OF, ELSE, END - зарезервированные слова (случай, из, иначе, конец);
<ключ_выбора> - ключ выбора;
<список_выбора> - одна или более конструкций вида:
<константа_выбора> : <оператор>;
<константа_выбора> - константа того же типа, что и выражение<ключ_выбopa> ;
<операторы> - произвольные операторы Турбо Паскаля.
Оператор выбора работает следующим образом. Вначале вычисляется значение выражения <ключ_выбора>, а затем в последовательности операторов <список_выбора> отыскивается такой, которому предшествует константа, равная вычисленному значению. Найденный оператор выполняется, после чего оператор выбора завершает свою работу. Если в списке выбора не будет найдена константа, соответствующая вычисленному значению ключа выбора, управление передается операторам, стоящим за словом ELSE. Часть ELSE <оператор> можно опускать. Тогда при отсутствии в списке выбора нужной константы ничего не произойдет и оператор выбора просто завершит свою работу.
Составим программу (пример 5.5), имитирующую работу микрокалькулятора. Программа вводит две строки: первая содержит два произвольных числа, разделенных пробелом, вторая - символ арифметического действия, например:
2 2
*
или
18.35 0.12
/
Над введенными числами осуществляется соответствующее действие и результат выводится на экран. Признаком конца работы программы служит ввод любого символа, отличного от +,-,*, /.
Пример 5.5
Program Calc;
{Программа вводит два числа в первой строке и один из знаков +, -, *, / - во второй и выводит на экран результат соответствующего арифметического действия}
var
operation : Char; {Знак операции}
х, у, z : Real; {Операнды и результат}
stop : Boolean; {Признак ошибочной операции и останова}
begin
stop := false;
repeat
WriteLn; {Пустая строка-разделитель}
Write('x,y= ' ) ;
ReadLn(x,y);
Write('операция: ') ;
ReadLn(operation);
case operation of
'+' : z : = x + y;
'-' : z : = x - y;
'*' : z : = x * y;
'/' : z : = x / y;
else
stop := true;
end; {case..of}
if not stop then WriteLn('результат =',z)
until stop
end.
Любому из операторов списка выбора может предшествовать не одна, а несколько констант выбора, разделенных запятыми. Например, следующая программа при вводе одного из символов: у или Y выведет на экран слово «Да», а при вводе n или N - слово «Нет»:
var
ch:Char;
begin
ReadLn(ch);
case ch of
'n','N':WriteLn('Нет');
'y','Y':WriteLn('Да')
end
end.
5.5 Метки и операторы перехода.
Можно теоретически показать, что рассмотренных операторов вполне достаточно для написания программ любой сложности. В этом отношении наличие в языке операторов перехода кажется излишним. Более того, современная технология структурного программирования основана на принципе «программировать без GOTO»: считается, что злоупотребление операторами перехода затрудняет понимание программы, делает ее запутанной и сложной в отладке.
Тем не менее, в некоторых случаях использование операторов перехода может упростить программу.
Оператор перехода имеет вид:
GOTO <метка>.
Здесь GOTO - зарезервированное слово (перейти [на метку]); <метка> - метка.
Метка в Турбо Паскале - это произвольный идентификатор, позволяющий именовать некоторый оператор программы и таким образом ссылаться на него. В целях совместимости со стандартным языком Паскаль в языке Турбо Паскаль допускается в качестве меток использование также целых чисел без знака.
Метка располагается непосредственно перед помечаемым оператором и отделяется от него двоеточием. Оператор можно помечать несколькими метками, которые в этом случае отделяются друг от друга двоеточием. Перед тем как появиться в программе, метка должна быть описана. Описание меток состоит из зарезервированного слова LABEL (метка), за которым следует список меток:
label
loop, 1b1, 1b2;
begin
...
goto 1b1;
...
loop: ...
...
1b1:1b2: ...
...
goto 1b2;
…
end.
Действие оператора GOTO состоит в передаче управления соответствующему меченному оператору.
При использовании меток необходимо руководствоваться следующими правилами:
метка, на которую ссылается оператор GOTO, должна быть описана в разделе описаний и она обязательно должна встретиться где-нибудь в теле программы;
метки, описанные в процедуре (функции), локализуются в ней, поэтому передача управления извне процедуры (функции) на метку внутри нее невозможна.
Лекции №6
Тема: Процедуры и функции.
Цель: Дать представление о процедурах и функциях, иметь представление о локальных и глобальных переменных.
План:
6.1 Локализация имен.
6.2 Описание программы.
6.3 Параметры.
6.4 Рекурсия и опережающее описание.
Процедуры и функции представляют собой важный инструмент Турбо Паскаля, позволяющий писать хорошо структурированные программы. В структурированных программах обычно легко прослеживается основной алгоритм, их нетрудно понять любому читателю, они проще в отладке и менее чувствительны к ошибкам программирования. Все эти свойства являются следствием важной особенности процедур (функций), каждая из которых представляет собой во многом самостоятельный фрагмент программы, связанный с основной программой лишь с помощью нескольких параметров. Самостоятельность процедур (функций) позволяет локализовать в них все детали программной реализации того или иного алгоритмического действия и поэтому изменение этих деталей, например, в процессе отладки обычно не приводит к изменениям основной программы.
Многие примеры в этой книге невелики по размерам (не более 30-40 строк), поэтому написать такие программы можно и без процедур. Иное дело - создание крупных программ в сотни, тысячи и десятки тысяч строк. Писать такие программы как нечто единое целое, без расчленения на относительно самостоятельные фрагменты, т.е. без структурирования, просто невозможно. Практически во всех языках программирования имеются средства структурирования. Языки, в которых предусмотрены такие механизмы, называются процедурно-ориентированными. К их числу принадлежит и Турбо Паскаль.
Процедурой в Турбо Паскале называется особым образом оформленный фрагмент программы, имеющий собственное имя. Упоминание этого имени в тексте программы приводит к активизации процедуры и называется ее вызовом. Сразу после активизации процедуры начинают выполняться входящие в нее операторы, после выполнения последнего из них управление возвращается обратно в основную программу и выполняются операторы, стоящие непосредственно за оператором вызова процедуры (рис.6.1).
Рис.6.1. Взаимодействие вызывающей программы и процедуры
6.1 Локализация имен
Напомню, что вызов подпрограммы осуществляется простым упоминанием имени процедуры в операторе вызова процедуры или имени функции в выражении. При использовании расширенного синтаксиса Турбо Паскаля функции можно вызывать точно так же, как и процедуры. Как известно, любое имя в программе должно быть обязательно описано, перед тем как оно появится среди исполняемых операторов. Не делается исключения и в отношении подпрограмм: каждую свою процедуру и функцию программисту необходимо описать в разделе описаний.
Описать подпрограмму - это значит указать ее заголовок и тело. В заголовке объявляются имя подпрограммы и формальные параметры, если они есть. Для функции, кроме того, указывается тип возвращаемого ею результата. За заголовком следует тело подпрограммы, которое, подобно программе, состоит из раздела описаний и раздела исполняемых операторов. В разделе описаний подпрограммы могут встретиться описания подпрограмм низшего уровня, в тех - описания других подпрограмм и т.д.
Рис.6.2. Пример структуры программы
Вот какую иерархию описаний получим, например, для программы, структура которой изображена на рис.6.2 (для простоты считается, что все подпрограммы представляют собой процедуры без параметров):
Program ...;
Procedure А;
Procedure A1;
...
begin
...
end; {A1};
Procedure A2;
...
begin
...
end; {A2};
begin {A}
...
end; {A};
Procedure В;
Procedure B1;
...
begin {B};
...
end;
Procedure B2 ;
Procedure B21;
...
begin {B21};
...
end; {B21}
begin
...
end; {B2};
begin {B}
...
end; {B};
begin {Основная программа}
...
end; {Конец основной программы};
и т.д.
Подпрограмма любого уровня имеет обычно множество имен констант, переменных, типов и вложенных в нее подпрограмм низшего уровня. Считается, что все имена, описанные внутри подпрограммы, локализуются в ней, т.е. они как бы «невидимы» снаружи подпрограммы. Таким образом, со стороны операторов, использующих обращение к подпрограмме, она трактуется как «черный ящик», в котором реализуется тот или иной алгоритм. Все детали этой реализации скрыты от глаз пользователя подпрограммы и потому недоступны ему. Например, в рассмотренном выше примере из основной программы можно обратиться к процедурам А и В, но нельзя вызвать ни одну из вложенных в них процедур А1, А2, В1 и т.д.
Сказанное относится не только к именам подпрограмм, но и вообще к любым именам, объявленным в них - типам, константам, переменным и меткам. Все имена в пределах подпрограммы, в которой они объявлены, должны быть уникальными и не могут совпадать с именем самой подпрограммы.
При входе в подпрограмму низшего уровня становятся доступными не только объявленные в ней имена, но и сохраняется доступ ко всем именам верхнего уровня. Образно говоря, любая подпрограмма как бы окружена полупрозрачными стенками: снаружи подпрограммы мы не видим ее внутренности, но, попав в подпрограмму, можем наблюдать все, что делается снаружи. Так, например, из подпрограммы В21 мы можем вызвать подпрограмму А, использовать имена, объявленные в основной программе, в подпрограммах В и В2, и даже обратиться к ним. Любая подпрограмма может, наконец, вызвать саму себя - такой способ вызова называется рекурсией.
Пусть имеем такое описание:
Program ..;
var V1 : ... ;
Procedure A;
var V2 :...;
...
end {A};
Procedure B;
var V3 :...;
Procedure Bl;
var V4 :...;
Procedure В11;
var V5;
...
Из процедуры В11 доступны все пять переменных V1,...,V5, из процедуры В1 доступны переменные V1,..., V4, из центральной программы - только V1.
При взаимодействии подпрограмм одного уровня иерархии вступает в силу основное правило Турбо Паскаля: любая подпрограмма перед ее использованием должна быть описана. Поэтому из подпрограммы В можно вызвать подпрограмму А, но из А вызвать В невозможно (точнее, такая возможность появляется только с использованием опережающего описания, см. п.8.6.) Продолжая образное сравнение, подпрограмму южно уподобить ящику с непрозрачными стенками и дном и полупрозрачной крышей: из подпрограммы можно смотреть только «вверх» и нельзя «вниз», т.е. подпрограмме доступны только те объекты верхнего уровня, которые описаны до описания данной подпрограммы. Эти объекты называются глобальными по отношению к подпрограмме.
В отличие от стандартного Паскаля в Турбо Паскале допускается произвольная последовательность описания констант, переменных, типов, меток и подпрограмм. Например, раздел VAR описания переменных может появляться в пределах раздела описаний одной и той же подпрограммы много раз и перемежаться с объявлениями других объектов и подпрограмм. Для Турбо Паскаля совершенно безразличен порядок следования и количество разделов VAR, CONST, TYPE, LABEL, но при определении о6ласти действия этих описаний следует помнить, что имена, описанные ниже по тексту программы, недоступны из ранее описанных подпрограмм, например:
var V1 : ...;
Procedure S;
var V2 : ...;
end {S};
var V3 :...;
...
Из процедуры S можно обратиться к переменным V1 и V2, но нельзя использовать V3, так как описание V3 следует в программе за описанием процедуры S.
Имена, локализованные в подпрограмме, могут совпадать с ранее объявленными глобальными именами. В этом случае считается, что локальное имя «закрывает» глобальное и делает его недоступным, например:
var
i:Integer;
Procedure P;
var
i:Integer;
begin
writeln(i)
end {P};
begin
i:=1;
P
end.
Что напечатает эта программа? Все, что угодно: значение внутренней переменной I при входе в процедуру Р не определено, хотя одноименная глобальная переменная имеет значение 1. Локальная переменная «закроет» глобальную и на экран будет выведено произвольное значение, содержащееся в неинициированной внутренней переменной. Если убрать описание
var
i:Integer;
из процедуры Р, то на экран будет выведено значение глобальной переменной I, т.е. 1. Таким образом, одноименные глобальные и локальные переменные - это разные переменные. Любое обращение к таким переменным в теле подпрограммы трактуется как обращение к локальным переменным, т.е. глобальные переменные в этом случае попросту недоступны.
6.2 Описание программы
Описание подпрограммы состоит из заголовка и тела подпрограммы.
Заголовок
Заголовок процедуры имеет вид: PROCEDURE <имя> [ (<сп. ф. п . >) ] ;
Заголовок функции: FUNCTION <имя> [ (<сп.ф.п.>)] : <тил>;
Здесь <имя> - имя подпрограммы (правильный идентификатор);
<сп.ф.п.> - список формальных параметров;
<тип> - тип возвращаемого функцией результата.
6.3 Параметры
Список формальных параметров необязателен и может отсутствовать. Если же он есть, то в нем должны быть перечислены имена формальных параметров и их типы, например:
Procedure SB(a:Real; b:Integer; c:Char);
Как видно из примера, параметры в списке отделяются друг от друга точками с запятой. Несколько следующих подряд однотипных параметров можно объединять в подсписки, например, вместо
Function F(a: Real; b: Real): Real;
можно написать проще:
Function F(a,b: Real): Real;
Операторы тела подпрограммы рассматривают список формальных параметров как своеобразное расширение раздела описаний: все переменные из этого списка могут использоваться в любых выражениях внутри подпрограммы. Таким способом осуществляется настройка алгоритма подпрограммы на конкретную задачу.
Рассмотрим следующий пример. В языке Турбо Паскаль нет операции возведения в степень, однако с помощью встроенных функций LN(X) и ЕХР(Х) нетрудно реализовать новую функцию с именем, например, POWER, осуществляющую возведение любого вещественного числа в любую вещественную степень. В программе (пример 6.2.1) вводится пара чисел X и Y и выводится на экран дисплея результат возведения X сначала в степень +Y, а затем - в степень -Y. Для выхода из программы нужно ввести Ctrl-Z и Enter.
Пример 6.2.1
var
х,у:Real;
Function Power (a,b:Real):Real;
begin {Power}
if a>0 then
Power:=exp(b*In(a))
else
if a<0 then
Power:=exp(b*ln(abs(a))
else
if b=0 then
Power:=1
else
Power:=0
end {Power} ;
{--------------------}
begin {main}
repeat
readln(x,y) ;
writeln(Power(x,y):12:10, Power(x, -y):15:10)
until EOF
end {main} .
Для вызова функции POWER мы просто указали ее в качестве параметра при обращении к встроенной процедуре WRITELN. Параметры X и Y в момент обращения к функции - это фактические параметры. Они подставляются вместо формальных параметров А и В в заголовке функции и затем над ними осуществляются нужные действия. Полученный результат присваивается идентификатору функции - именно он и будет возвращен как значение функции при выходе из нее. В программе функция POWER вызывается дважды - сначала с параметрами Х и Y, а затем Х и -Y, поэтому будут получены два разных результата.
Дата добавления: 2015-02-19; просмотров: 797;