Предоставление дополнительной информации транслятору о размещения подпрограмм как одно из основных назначений программной директивы. Анализ методики передачи параметров-переменных в подпрограмму фактически через стек на языке программирования Pascal.
При низкой оригинальности работы "Особенности объявления функций на языке программирования Pascal", Вы можете повысить уникальность этой работы до 80-100%
Для вызова функции из основной программы или другой подпрограммы следует в выражении, где необходимо использовать значение функции, указать имя функции со списком фактических параметров, которые должны совпадать по количеству и типам с формальными параметрами функции, например: Part: = Sqr(T)/Factorial(i); Формальные и фактические параметры подпрограммы указывают, с какими параметрами следует обращаться к этой подпрограмме (количество параметров, их последовательность, типы). Чтобы правильно записать этот заголовок, следует в основной программе ввести тип-массив, а затем использовать его в заголовке: Type TARR = array[1..100] of Real; Параметры-значения передаются основной программой в подпрограмму через стек в виде их копий и, следовательно, собственный параметр программы подпрограммой измениться не может. Модифицируем подпрограмму используя в качестве первого параметра параметр-переменную: Fuction max(var mas: TARR; n:byte): integer;В данной курсовой работе был рассмотрен один из способов решения задач на языке программирования Паскаль, а именно функции.
Введение
В данной курсовой работе будет рассмотрен один из способов решения задач на языке программирования Паскаль, а именно функции. Будут рассмотрены виды функций, примеры решения задач с функциями.
Актуальность.
В нынешнее время, несмотря на то, что Паскаль существует уже очень давно, он остается актуальным. Многие программисты предпочитают писать программы именно на Паскале. Также этот язык программирования используется в учебных целях для обучения программированию. Ни один из других языков программирования не может похвастаться такими возможностями. И еще многие современные языки программирования были созданы на Паскале, либо основаны на нем.
Краткий обзор специальной литературы.
Здесь представлен краткий обзор литературы использованной в данной курсовой работе. А именно учебно-справочное издание по Turbo Pascal, которое дает исчерпывающую информацию о данном языке программирования, учебное пособие, которое дает дополнительные сведения, а также информация с интернет ресурсов.
Цель и содержание поставленных задач.
В основной части раскрывается теоретический материал по данной теме с примерами решения задач. В практической части даны условия и решения задач.
Объект и предмет исследования.
Объектом и предметом исследования является функция в Turbo Pascal. Функция это программный объект, который задает вычислительную процедуру определения значения, зависимого от некоторых аргументов.
Методы исследования.
Методом исследования данной курсовой работы является теоретическим и практическим. В теоретической части раскрыта информация по данной теме. В практической части даны задачи и их решение.
1. Функции в паскале
Функция.
Подпрограмма-функция предназначена для вычисления какого-либо параметра. У этой подпрограммы два основных отличия от процедуры. Первое отличие функции в ее заголовке. Он состоит из слова function, за которым следует имя функции, далее в круглых скобках - список формальных параметров, затем через двоеточие записывается тип функции - тип возвращаемого параметра. Функция может возвращать параметры следующих типов: любого порядкового, любого вещественного, стандартного типа string, любого указателя, в том числе и типа char.
Второе отличие заключается в том, что в теле функции хотя бы раз имени функции должно быть присвоено значение.
Пример. Функция вычисления факториала числа N.
Function Factorial (N: Byte): Longint;
Fact: Longint;
I: Byte;
Begin
Fact: =N;
For I: =N-1 downto 2 do
Fact: =Fact*i
Factorial: =Fact
End;
Если имя функции внутри ее описания используется не в левой части оператора присваивания, то это означает, что функция вызывает себя рекурсивно.
Для вызова функции из основной программы или другой подпрограммы следует в выражении, где необходимо использовать значение функции, указать имя функции со списком фактических параметров, которые должны совпадать по количеству и типам с формальными параметрами функции, например: Part: = Sqr(T)/Factorial(i);
В этом операторе: Sqr(t) - вызов стандартной функции возведения в квадрат с фактическим параметром Т;
Factorial(i) - вызов функции, вычисляющей факториал с фактическим параметром i.
Формальные и фактические параметры.
Формальные и фактические параметры подпрограммы указывают, с какими параметрами следует обращаться к этой подпрограмме (количество параметров, их последовательность, типы). Они задаются в заголовке подпрограммы в виде списка, разбитого на группы, разделенные точками с запятыми. В группу формальных параметров включаются однотипные параметры одной категории.
Все формальные параметры можно разбить на четыре категории: · Параметры-значения (эти параметры основной подпрограммой не меняются);
· Параметры-переменные (эти параметры подпрограмма может изменить в основной программе);
· Параметры-константы (используются только в версии 7.0);
· Параметры-процедуры и параметры-функции (т.е. процедурного типа).
Для каждого формального параметра следует указать имя и, как правило, тип, а в случае параметра-переменной или параметра-константы - его категорию. Имена параметров могут быть любыми, в том числе и совпадать с именами объектов программы. Необходимо лишь помнить, что в этом случае параметр основной программы с таким именем становиться недоступным для непосредственного использования подпрограммой. Тип формального параметра может быть практически любым, однако в заголовке подпрограммы нельзя вводить новый тип. Например, нельзя писать: Function Max(A:array[1..100] of Real):Real;
Чтобы правильно записать этот заголовок, следует в основной программе ввести тип-массив, а затем использовать его в заголовке: Type TARR = array[1..100] of Real;
Function Max(A:TARR): Real;
При обращении к подпрограмме формального параметра заменяются на соответствующие фактические вызывающей программы или подпрограммы.
Параметры-значения.
Параметры-значения передаются основной программой в подпрограмму через стек в виде их копий и, следовательно, собственный параметр программы подпрограммой измениться не может. Параметр-значение указывается в заголовке своим именем и через двоеточие - типом. Тип параметра-значения может быть любым за исключением файлового.
Если параметров-значений одного типа несколько их можно объединить в одну группу, перечислив их имена через запятую, а затем уже указать общий тип. Как уже отмечалось выше, отдельные группы параметров отделяются друг от друга точкой с запятой.
Пример.
Procedure Inp(Max, Min: Real; N: Word);
Function Mult(X,Y: Integer): Real;
В качестве фактического параметра на месте параметра-значения при вызове подпрограммы может выступать любое выражение совместимого для присваивания, не содержащее файловую компоненту, например: Inp(Abs(Z), - Abs(T),2*K);
M:=Mult(X Y, X-Y);
MA:=Max(B,5);
Пример. Функция вычисления максимального элемента в массиве. Пусть в основной программе определен тип-массив, массив этого типа и переменная целого типа.
Type
TARR=array[1..100] of integer;
var massiv: TARR;
maxim:integer;
функция в этом случае может иметь вид: function max(mass: TARR; n: byte): integer;
var ma: integer;
i: byte;
begin ma:=mass[1];
for i:=2 to n do if ma<mas[1] then ma:=mas[1];
max:=ma;
end;
Теперь, например, для определения максимального числа из первых пяти чисел массива massiv и записи его в переменную maxim можно записать оператор: maxim:=max(massiv,5);
Следует иметь ввиду, что подпрограмма может работать только с массивами типа TARR. Для массивов другого типа придется создавать другую аналогичную подпрограмму. Кроме того, при работе подпрограммы в стеке будет создана копия исходного массива, что приводит к уменьшению быстродействия и заполнению стека излишней информацией.
Параметры-переменные.
При передаче параметров-переменных в подпрограмму фактически через стек передаются их адреса в порядке, объявленном в заголовке подпрограммы. Следовательно, подпрограмма имеет доступ к этим параметрам и может их изменять. Параметр-переменная указывается в заголовке подпрограммы аналогично параметру-значению, но только перед именем параметра записывается зарезервированное слово var. Действие слова var распространяется до ближайшей точки с запятой, т.е. в пределах одной группы.
Пример.
Procedure maxmin(a: TARR; var max, min:real; n:word);
Здесь max, min-параметры-переменные, a и n-параметры-значения. Тип параметров-переменных может быть любым, включая и файловый. При вызове подпрограммы на месте параметра-переменной в качестве фактического параметра должна использоваться переменная идентичного типа. Так, если формальный параметр имеет тип, определенный следующим образом:
Type TARR=array[1..] of integer;
То и фактический параметр должен быть переменной или типизированной константой типа TARR.
Пример. Функция вычисления максимального элемента в массиве. Модифицируем подпрограмму используя в качестве первого параметра параметр-переменную: Fuction max(var mas: TARR; n:byte): integer;
Var ma:integer;
I:byte;
Begin
Ma:=mas[i];
For i:=2 to n do
If ma< mas[i] then
Ma:=mas[i];
Max:=ma;
End;
Этот вариант лучше предыдущего тем, что в данном случае в стеке не создается копия исходного массива, что улучшает быстродействие и экономит память. Однако при такой передаче параметра возможно его нежелательное изменение (такой вариант передачи параметра допустим только в таких небольших подпрограммах, как в данном примере, когда программист может проконтролировать отсутствие несанкционированного изменения параметра). Недостаток же, связанный с тем, что подпрограмма может работать только с одним типом массивов, остается.
Параметры-константы.
Часто в качестве параметра в подпрограмму следует передать ту или иную переменную, но изменять ее подпрограмма не должна. В этом случае нежелательно передавать этот параметр как параметр-переменную. Можно его передать как параметр-значение, однако, если эта переменная имеет большой размер (массив, запись и т.д.), то копия такого параметра займет большую часть стека и даже может его переполнить. Это же приводит и к уменьшению быстродействия программы. В этой ситуации параметр лучше передать как параметр-константу. Такой параметр, если он сконструированного типа, передается своим адресом, но предусматривается защита от его изменения. Изменять параметр-константу можно только в версии 7.0.
Параметр-константа указывается в заголовке подпрограммы аналогично параметру-значению, но перед именем параметра записывается зарезервированное слово const. Действие слова const распространяется до ближайшей точки с запятой, т.е. в пределах одной группы.
Пример.
Function newstring(const s:string):string;
Тип параметра значения может быть любым за исключением файлового. При вызове подпрограммы на месте параметра-переменной в качестве фактического параметра можно использовать любое выражение совместимого для присваивания типа, не содержащего файловую компоненту.
Параметр-константу нельзя передавать в другую подпрограмму в качестве фактического параметра.
Пример. Функция вычисления максимального элемента в массиве. В примере используем в качестве первого параметра параметр-константу: Fuction max(const: TARR; n:byte): integer;
Var ma:integer;
I:byte;
Begin
Ma:=mas[i];
For i:=2 to n do
If ma< mas[i] then
Ma:=mas[i];
Max:=ma;
End;
Параметры без типа.
В Turbo Pascal можно использовать параметры-переменные и параметры-константы без указания типа. В этом случае фактический параметр может быть переменной любого типа, а ответственность за правильность использования того или иного параметра возлагается на программиста.
Пример.
Function equal(var param1, param2; len:word): Boolean;
транслятор программный стек
Здесь param1 и param2 -параметры-переменные без типа (вместо них можно использовать, например, любые переменные простого типа, типа-массив; типа-запись и т.д.); len-парметр-значение.
Следует иметь ввиду, что параметр без типа внутри подпрограммы типа не имеет и его перед использованием следует преобразовать к конкретному типу, применяя идентификатор соответствующего типа, при этом полученный результат может быть любого размера.
Пример. Функция вычисления максимального элемента в массиве.
Рассмотрим другой вариант подпрограммы используя в качестве первого параметра параметр-переменную без типа: Fuction max(var mas; n:byte): integer;
type
TARRAY=array[1..maxint] of integer;
var ma:integer;
I:byte;
Begin
Ma:=TARRAY(mas)[i];
For i:=2 to n do
If ma< TARRAY(mas)[i] then
Ma:=TARRAY(mas)[i];
Max:=ma;
End;
В этом случае в качестве первого передаваемого параметра можно использовать любой массив (и не только массив), так что подпрограмма становится более универсальной. Тем не менее здесь необходимо передавать в качестве второго параметра фактический размер информации, что не очень удобно.
Массивы и строки открытого типа.
В версии 7.0 можно в качестве параметров-переменных использовать массивы и строки открытого типа, у которых не задаются размеры. В качестве фактического параметра в этом случае можно использовать массив или строку любого размера, однако массив должен состоять из тех же компонент, что и компоненты открытого массива. Такие параметры введены для того, чтобы подпрограмма могла обрабатывать массив или строку любого размера. Фактический размер массива в этом случае может быть определен с помощью функции High. Открытый массив задается, как и обычный массив, но только без указания индекса. Следует иметь ввиду, что индексация элементов открытого массива всегда начинается с нуля, а максимальный индекс элемента равен значению функции High.
Пример. Функция вычисления максимального элемента в массиве. Рассмотрим вариант подпрограммы используя в качестве передаваемого параметра массив открытого типа: Fuction max(var mas: array of integer): integer;
Var ma:integer;
I:byte;
Begin
Ma:=mas[0];
For i:=2 to high(mas) do
If ma< mas[i] then
Ma:=mas[i];
Max:=ma;
End;
В этом примере в подпрограмму передается только один параметр и она может работать с любым одномерным массивом целых чисел. Однако следует иметь ввиду, что при работе подпрограммы для открытого массива в стеке опять-таки создается его копия, что может его переполнять.
Разновидность открытого массива-открытая строка, которая может задаваться либо с помощью стандартного типа OPENSTRING, либо с помощью типа string и использования ключа компилятора.
Параметры-процедуры и параметры-функции.
Передаваемым параметром может быть также параметр-процедура или параметр-функция, т.е. записывается без зарезервированного слова var.
В качестве фактического параметра в этом случае используется соответствующая процедура или функция, имеющая необходимое количество параметров требуемых типов.
Для параметров-процедур и параметров-функций существуют те же правила, что и для других переменных процедурного типа: подпрограммы должны компилировать с ключом {$f } или иметь директиву far, не должны быть стандартными подпрограммами, не должны обновляться внутри других подпрограмм, не иметь директив inline или interrupt.
Директивы программ.
Директивы дают дополнительную информацию транслятору о размещения подпрограмм.
Директива FORWARD.
Если одна подпрограмма использует другую, а та, в свою очередь, эту первую, то возникает проблема размещения этих подпрограмм в программе (ни одну из них нельзя поместить перед другой). Чтобы устранить это противоречие, используется директива forward, позволяющая как бы разбить на две части одну из подпрограмм.
При использовании директивы forward сначала записывается полный заголовок первой подпрограммы. Тело этой подпрограммы заменяется директивой forward. Затем полностью описывается вторая подпрограмма, а уже после этого полностью описывается первая подпрограмма. При этом можно записать сокращенный заголовок подпрограммы, который включает слово procedure или function и ее имя. Список формальных параметров и тип подпрограммы (если это подпрограмма-функция) не указывается.
Директиву forward можно использовать и просто для более удобного размещения подпрограмм: сначала описать все заголовки, а затем - сами подпрограммы. Эту директиву не следует использовать в модулях для подпрограмм, объявленных в интерфейсе модуля.
Директивы FAR и NEAR.
Как правило, компилятор Turbo Pascal автоматически выбирает адресацию к подпрограмме. Например, если подпрограмма находится в одном файле с основной программой, то она компилируется с «ближним» адресом входа и возврата, состоящим только из смещения в текущем сегменте, а если она находится в модуле, то формируется «дальний» адрес, состоящий из адреса сегмента и смещения.
В некоторых случаях нужен нестандартный вариант компиляции. Так, например, если подпрограмма используется для переменных процедурного типа, она независимо от своего расположения должна компилироваться с получением «дальнего» адреса. В этом случае в подпрограмме можно использовать директиву far, которая сообщит компилятору, что нужно формировать именно такой адрес. Эта директива эквивалентна ключу компилятора, однако в отличие от этого ключа действие директивы распространяется только на одну подпрограмму.
Реже используется директива near, которая сообщает компилятору, что подпрограмму следует компилировать с получением именно такого адреса. Эта директива эквивалентна ключу компилятора, который выбирается по умолчанию. Действие директивы распространяется только на одну подпрограмму.
Директива EXTERNAL.
Директива external позволяет использовать в программе подпрограммы, написанные на языке ассемблера и скомпилированные отдельно. Эти подпрограммы должны быть скомпонованы с основной программой, используя ключ. Здесь имя файла - имя того файла (с расширением .OBJ), в котором находятся скомпилированные объектные модули подпрограмм, написанных на языке ассемблер.
Директиву external следует использовать, если подпрограммы на ассемблере имеют большой размер и их лучше скомпилировать отдельно, а не использовать встроенный ассемблер или писать их в кодах процессора, используя директиву inline.
Директива ASSEMBLER.
Директива assembler позволяет написать подпрограмму полностью на языке ассемблера. При этом во время компиляции подпрограмма будет автоматически скомпилирована встроенным ассемблером пакета Turbo Pascal. При отладке такой подпрограммы можно использовать встроенный отладчик пакета.
Пример. Функция, определяющая максимальное из двух чисел.
Fuction maxtwo(x,y:integer):integer; assembler;
Asm
Mov ax,x
Cmp ax,y
Jg @1
Mov ax,y
@1
End;
Директива INLINE.
Директива позволяет включить в текст программы команды, записанные непосредственно в машинных кодах. В отличие от других подпрограмм подпрограмма с директивой inline непосредственно добавляется всюду, где есть ее вызов (фактически она является макроопределением). Такие подпрограммы могут иметь параметры, которые можно использовать в тексте подпрограммы, получая их из стека. Машинные коды в процедуре записываются в круглых скобках побайтно через прямой слэш (/). Пример. Функция, вычисляющая максимальное из двух чисел.
Fuction maxtwo(x,y:integer):integer;
Inline(
$58/ {pop ax -получение y из стека}
$5a/ {pop dx -получение x из стека}
$3b/$c2/ {cmp ax, dx}
$7f/$02/ {jg -переход через одну команду}
$8b/$c2; {mov ax, dx}
В связи с тем, что такие подпрограммы являются макроопределениями и помещаются всюду, где осуществляется их вызов, нет необходимости организовывать стандартный вход в подпрограмму и выход из нее с сохранением и восстановлением регистров. По этой причине такие подпрограммы являются довольно эффективными по быстродействию и объему занимаемой памяти. Однако использовать их целесообразно лишь в случаях, когда они являются достаточно короткими (не более десятка команд).
Директива INTERRUPT.
Директива interrupt предназначена для процедур, обрабатывающих прерывания. Такие процедуры имеют стандартный заголовок.
В заголовке отдельные параметры можно опускать (но только с начала списка), промежуточные параметры удалять нельзя.
Нельзя в заголовке процедуры обработки прерываний записывать и какие-либо другие параметры.
Рекурсивные процедуры и функции.
Язык Паскаль допускает, чтобы подпрограмма вызывала саму себя (рекурсивное обращение). Эта возможность связана с тем, что при каждом новом обращении к подпрограмме параметры, которые она использует, заносятся в стек, причем параметры предыдущего обращения также сохраняются.
В ряде случаев (обычно для рекурсивных алгоритмов) рекурсивное оформление подпрограммы может быть более компактным и эффективным, но не следует забывать об опасности переполнения стека.
Пример. Вариант функции, рекурсивно вычисляющей факториал числа N.
Function factorial(n:byte): longint;
Begin
If n in [0..1];
Then factorial:=1;
Else factorial:=n*factorial(n-1);
End;
Функции.
Функция - это подпрограмма (аналогично процедуре), результатом которой является единственное скалярное (или ссылочное, о чем позднее) значение, используемое как составная часть выражения. С функцией не связывается понятие оператора функции. Ее вызов или активизация осуществляется с помощью имени функции, которое используется как операнд, т.е. непосредственно из выражения.
Описание функции отличается от описания процедуры только заголовком, который имеет вид: ::= FUNCTION : ?
FUNCTION
(): ::= ?
Первый вариант заголовка функции по аналогии с процедурой определяет функцию без параметров. Все сказанное выше о формальных и фактических параметрах процедуры как о механизме передачи объектов, связывающих ее с вызывающей программой (процедурой или функцией) в полной мере может быть отнесено и к функции с параметрами.
Механизм, с помощью которого в выражение из вызывающей программы (процедуры или функции) возвращается результат, для функции организуется иначе, чем для процедуры. При активизации функции в памяти резервируется ячейка под переменную с именем функции и типом, соответствующим определению типа результата в ее заголовке. Внутри блока функции должно быть выполнено присваивание этой переменной вычисленного значения. Именно это значение и используется как операнд в вызывающем функцию выражении.
В следующем ниже примере описание function Expo, предназначеной для вычисления S=xy, для -n ? y ? n, т.е. степенной функции вещественного аргумента для произвольного целого показателя степени. program PRIM4_5;
var
P : Real;
q : Integer;
function Expo(X : Real; Y : Integer) : Real;
var
I : Integer;
begin if y<0 then begin
X := 1/X;
Y :=-Y end;
Expo :=1;
for I:=1 to Y do
Expo := Expo *X end: {Expo}
BEGIN
WRITELN (‘Введите аргумент степенной функции х= ’);
READLN(P);
WRITELN (‘Введите показатель степени y=’);
READLN(Q);
WRITELN (x,’ в степени ‘,y,’ равно ‘,Expo(P,Q) end. {Prim4_5}
До этого момента рассматривались только параметры-переменные и параметры-значения. Но, в соответствии с конструкцией заголовка, параметрами могут быть сами процедуры или функции. Эти параметры, как уже упоминалось, вводятся с помощью спецификаторов procedure или FUNCTION.
Приведенная ниже программа ищет точку пересечения некоторой функции с осью абсцисс (нулевое значение) методом деления пополам; функция задается в момент обращения. Очевидно, что такая функция должна быть либо стандартной (библиотечной), либо описанной в тексте программы;. program prim_22;
var
A, B, Eps : Real; {[A,B]- интервал, Eps -погрешность}
FUNCTION zero (function F : Real; X, Y : Real) : Real;
var u,z : real;
S : Boolean;
begin
S := F(X)<0;
repeat
U :=(X Y)/2;
Z := F(U);
if (Z<0) = S then
X :=U else
Y :=U until Abs (X-Y) < Eps;
Zero :=U end; {Zero} begin
WRITE (‘Введите значения гранц интервала ’);
READLN (a,b);
Write (‘Введите допустимую погрешность ’);
READLN (Eps);
WRITELN (Zero (SIN,A,B));
WRITELN (Zero (Cos,A,B)) end. {Prim4_6}
Если в разделе операторов процедуры или функции встречается присваивание нелокальной переменной или параметру-переменной, то это, как уже упоминалось, называется побочным эффектом. Целесообразность таких присваиваний сомнительна, может приводить к нежелательным эффектам и затрудняет верификацию программ. Естественным исключением являются присваивания параметрам-переменным процедуры, необходимые для возврата результатов в вызывающую программу. Пример проявления побочного эффекта процедуры хорошо иллюстрирует программа Prim4_3, В качестве примера-теста побочного эффекта функции можно использовать следующую программу: program Prm4_6;
Для исключения ошибок, связанных с побочным эффектом, желательно: избегать в блоке процедуры или функции присваиваний глобальным переменным;
не использовать в процедурах и функциях идентичных имен для локальных и глобальных переменных; в этом случае неописанная локальная переменная вызовет сообщение об ошибке; в противном случае такого сообщения не будет (глобальная с тем же именем описана), а поведение программы непредсказуемо.
2. Листинг программы
1. Найти номера четных элементов массива.
Program m1;
Const n: =10;
Var a: =array [1..n] of integer;
I:integer;
Begin
For i: =1 to n do
Begin
Write (‘a=’); readln (a[i]);
End;
For i: =1 to n do
If a[i] mod 2=0 then write (i);
End.
2. Найти номер последнего отрицательного элемента массива.
Program m2;
Const n: =10;
Var a: array [n..1] of integer;
I:integer;
Begin while (i>=1) and (a[i]>=0) do dec (i); {i:=i-1;}
Write (i);
End;
readln;
End.
3. Найти номер максимального элемента массива.
Program m3;
Const n: =10;
Var a: array [1..n] of integer;;
I: integer;
Begin
While (i>1) and (a[i]>0) do dec (i); {i:=i 1;}
Write (i);
End;
readln;
End.
4. Написать подпрограмму-функцию степени ax , где a, x-любые числа. Воспользуемся формулой: ax=ex ln a Program p2;
Var f, b, s, t, c, d: real;
Function stp (a, x: real): real;
Var y: real;
Begin
Y: =exp (x*ln (a));
Stp: =y;
End;
Begin
D: =stp (2.4, 5);
Writeln (d, stp (5, 3.5));
Read (f, b, s, t); c: =stp (f, s) stp (b,t);
Writeln (c);
End.
Вывод
В данной курсовой работе был рассмотрен один из способов решения задач на языке программирования Паскаль, а именно функции. Были рассмотрены виды функций, примеры решения задач с функциями.
Размещено на .ru
Вы можете ЗАГРУЗИТЬ и ПОВЫСИТЬ уникальность своей работы