2.7. Процедуры и функции
Процедуры и функции представляют собой самостоятельные программные объекты, реализующие некоторую часть общего алгоритма программы. В силу этого, их реализация рассматривается как совокупность механизмов передачи данных и управления. Передача управления в случае процедуры представляет собой указание имени процедуры в какой либо точке программы, для функции √ употребление ее имени в каком либо выражении. Передача данных в Borland Pascal связана с передачей параметров по четырем схемам, которые формируют следующие классы параметров:
1. параметры-значения: параметры без предшествующего ключевого слова var;
2. параметры-константы: параметры, перед которыми следует ключевое слово const и за которыми следует тип;
3. типизированные параметры-переменные: параметры, перед которыми стоит ключевое слово var и за которыми следует тип;
4. нетипизированные параметры: параметры, перед которыми стоит ключевое слово var или const за которыми не следует тип.
Кроме того, выделяется класс открытых параметров. Его образуют открытые строковые параметры-переменные, описанные с помощью идентификатора OpenString или с использованием ключевого слова string в состоянии {$P+} и открытым параметры-массивы, которые представляют собой значение, константу или параметр-переменную, описанную с помощью синтаксиса array of T.
Рассмотрим каждую группу типов параметров подробнее.
Параметры-значения
Формальный параметр-значение обрабатывается как локальная по отношению к процедуре или функции переменная, за исключением того, что он получает свое начальное значение из соответствующего фактического параметра при активизации процедуры или функции. Изменения, которые претерпевает формальный параметр-значение, не влияют на значение фактического параметра. Соответствующее фактическое значение параметра-значения должно быть выражением и его значение не должно иметь файловый тип или какой-либо структурный тип, содержащий в себе файловый тип. Фактический параметр должен иметь тип, совместимый по присваиванию с типом формального параметра-значения. Если параметр имеет строковый тип, то формальный параметр будет иметь атрибут размера, равный 255.
Параметры-константы
Формальные параметры-константы работают аналогично локальной переменной, доступной только по чтению, которая получает свое значение при активизации процедуры или функции от соответствующего фактического параметра. Присваивания формальному параметру-константе не допускаются. Формальный параметр-константа также не может передаваться в качестве фактического параметра другой процедуре или функции. Параметр-константа, соответствующий фактическому параметру в операторе процедуры или функции, должен подчиняться тем же правилам, что и фактическое значение параметра. В тех случаях, когда формальный параметр не изменяет при выполнении процедуры или функции своего значения, вместо параметра-значения следует использовать параметр-константу. Параметры-константы позволяют при реализации процедуры или функции защититься от случайных присваиваний формальному параметру. Кроме того, для параметров структурного и строкового типа компилятор при использовании вместо параметров-значений параметров-констант может генерировать более эффективный код.
Параметры-переменные
Параметр-переменная используется, когда значение должно передаваться из процедуры или функции вызывающей программе. Соответствующий фактический параметр в операторе вызова процедуры или функции должен быть ссылкой на переменную. При активизации процедуры или функции формальный параметр-переменная замещается фактической переменной, любые изменения в значении формального параметра-переменной отражаются на фактическом параметре. Внутри процедуры или функции любая ссылка на формальный параметр-переменную приводит к доступу к самому фактическому параметру. Тип фактического параметра должен совпадать с типом формального параметра-переменной (вы можете обойти это ограничение с помощью нетипизированного параметра-переменной). Следует отметить, что файловый тип может передаваться только, как параметр-переменная. Директива компилятора $P управляет смыслом параметра-переменной, описываемого с ключевым словом string. В состоянии по умолчанию ({$P-}) string соответствует строковому типу с атрибутом размера 255. В состоянии {$P+} string указывает, что параметр является открытым строковым параметром. При ссылке на фактический параметр-переменную, связанную с индексированием массива или получением указателя на объект, эти действия выполняются перед активизацией процедуры или функции.
Нетипизированные параметры
Когда формальный параметр является нетипизированным параметром-переменной, то соответствующий фактический параметр может представлять собой любую ссылку на переменную или константу, независимо от ее типа. Нетипизированный параметр, описанный с ключевым словом var, может модифицироваться, а нетипизированный параметр, описанный с ключевым словом const, доступен только по чтению. В процедуре или функции у нетипизированного параметра-переменной тип отсутствует, то есть он несовместим с переменными всех типов, пока ему не будет присвоен определенный тип с помощью присваивания типа переменной. Приведем пример нетипизированных параметров-переменных:
function IsEqual( var V1, V2; Size: word): Boolean;
type
Bytes= array[0..MaxInt] of byte;
var
Count: integer;
begin
Count:= 0;
while ( Count < size) and
( Bytes( V2 )[ Count ] <> Bytes( V1 )[ Count ] ) do
Inc( Count );
IsEqual:= Count= Size;
end;
Эта функция может использоваться для сравнения любых двух переменных любого размера:
type
Vector= array[1..10] of integer;
var
Vector1, Vector2: Vector;
Count: integer;
...
Equal( Vector1, Vector2, SizeOf( Vector ) ) {сравнивается Vесtor1 с Vесtor2}
...
Equal( Vector1, Vector2, SizeOf( Integer ) * Count ) {сравниваются первые Count элементов Vесtor1 с первыми Count элементами Vесtor2}
...
Equal( Vector1[ 1 ], Vector2[ 6 ], SizeOf( Integer ) * 5) {сравниваются первые 5 элементов Vесtor1 с последними пятью элементами Vесtor2}
...
Хотя нетипизированные параметры дают большую гибкость, пользоваться ими следует осторожно в силу того, что компилятор не может проверить допустимость операций с нетипизированными переменными.
Открытые параметры
Открытые параметры позволяют передавать одной и той же процедуре или функции строки и массивы различных размеров.
Открытые строковые параметры
Открытые строковые параметры могут описываться с помощью идентификатора OpenString, а также с помощью ключевого слова string в состоянии {$P+}. Идентификатор OpenString описывается в модуле System. Он обозначает специальный строковый тип, который может использоваться только в описании строковых параметров. В целях обратной совместимости OpenString не является зарезервированным словом и может, таким образом, быть переопределен как идентификатор, заданный пользователем. Когда обратная совместимость значения не имеет, для изменения смысла ключевого слова string можно использовать директиву компилятора {$P+}. В состоянии {$P+} переменная, описанная с ключевым словом string, является открытым строковым параметром. Для открытого строкового параметра фактический параметр может быть переменной любого строкового типа. В процедуре или функции атрибут размера (максимальная длина) формального параметра будет тем же, что у фактического параметра. Открытые строковые параметры ведут себя также как параметры-переменные строкового типа, только их нельзя передавать как обычные переменные другим процедурам или функциям. Однако их можно снова передать как открытые строковые параметры. В следующем примере параметр S процедуры AssignString - это открытый строковый параметр:
procedure AssignString( var S: OpenString );
begin
S := 'Sample for OpenString';
end;
Так как S - это открытый строковый параметр, AssignString можно передавать переменные любого строкового типа:
var
String1: string[ 10 ];
String2: string[ 30 ];
begin
...
AssignString( String1 ); {String1 := 'Sample for'}
...
AssignString( String2 ); {String2 := 'Sample for OpenString'}
...
end;
В AssingString максимальная длина параметра S та же самая, что у фактического параметра. Таким образом, в первом вызове AssingString при присваивании параметра S строка усекается, так как максимальная длина String1 равна 10. При применении к открытому строковому параметру стандартная функция Low возвращает 0, стандартная функция High возвращает описанную максимальную длину фактического параметра, а функция SizeOf возвращает размер фактического параметра. В следующем примере процедура FillStr заполняет строку заданным символом до ее максимальной длины. Функция High используется для получения максимальной длины отк╜рытого строкового параметра.
procedure FillStr(var S: OpenString; Ch: Char);
begin
S[ 0 ] := Chr( High( S ) ); {задает длину строки}
FillChar( S[ 1 ], High( S ), Ch ); {устанавливает число символов}
end;
Значения и параметры-константы, описанные с использованием идентификатора OpenString или ключевого слова string в состоянии {$P+}, не являются открытыми строковыми параметрами. Они ведут себя также, как если бы были описаны с максимальной длиной строкового типа 255, а функция Hight для таких параметров всегда возвращает 255.
Открытые параметры-массивы
Формальный параметр, описанный с помощью синтаксиса:
array of T
является открытым параметром-массивом. T должно быть идентификатором типа, а фактический параметр должен быть переменной типа T, или массивом, типом элементов которого является T. В процедуре или функции формальный параметр ведет себя так, как если бы он был описан следующим образом:
array[0..N - 1] of T
где N - число элементов в фактическом параметре. По существу, диапазон индекса фактического параметра отображается в диапазон целых чисел от 0 до N - 1. Если фактический параметр - это прос╜тая переменная типа T, то он интерпретируется как массив с одним элементом типа T. К открытому формальному параметру-массиву можно обращаться только по элементам. Присваивания всему открытому массиву не допускаются, и открытый массив может передаваться другим процедурам или функциям только как открытый параметр-массив или нетипизированный параметр-переменная. Открытый параметр-массив может быть параметром-значением, параметром-константой и параметром-переменной и имеет тот же смысл, что и обычные параметры-значения, параметры-константы и параметры-переменные. В частности, присваивания элементам формального открытого массива-константы не допускаются, а присваивания элементам формального открытого массива, являющегося параметром-значением, не влияют на фактический параметр. Для открытых массивов-значений компилятор создает в кадре стека процедуры или функции локальную копию фактического параметра. Таким образом, при передаче в качестве открытых параметров-значений больших массивов следует учитывать возможное переполнение стека. При применении к открытому параметру-массиву, стандартная функция Low возвращает 0, стандартная функция High возвращает индекс последнего элемента в фактическом параметре-массиве, а функция SizeOf возвращает размер фактического параметра-массива. Процедура EraseArray в следующем примере присваивает каждому элементу массива вещественное значение ноль, а функция SumOfArray вычисляет сумму всех элементов массива вещественных чисел. Поскольку в обоих случаях параметр A является открытым параметром-массивом, подпрограммы могут работать с любым массивом элементов типа Real:
procedure EraseArray(var A: array of Real);
var
Count: Word;
begin
for Count:= 0 to High( A ) do A[ Count ] := 0;
end;
function SumOfArray(const A: array of Real): Real;
var
Count: Word;
Sum : Real;
begin
Sum:= 0;
for Count:= 0 to High( A ) do Sum:= Sum + A[ Count ];
SumOfArray:= Sum;
end;
Когда типом элементов открытого параметра-массива является Char, фактический параметр может быть строковой константой. Например, с учетом предыдущего описания:
procedure OutputString(const S: array of Char);
var
Count: Integer;
begin
for Count:= 0 to High( S ) do
if S[ Count ] <> #0 then Write(S [ Count ]) else Break;
end;
допустимы следующие вызовы этой процедуры:
OutputString('Some text');
OutputString('A');
При передаче в качестве открытого параметра-массива пустая строка преобразуется в строку с одним элементом, содержащим символ NULL, поэтому оператор OutputString( '' ) идентичен оператору OutputString('#0').
В результате использования процедур и функций информационная среда программы статически делится на формальные параметры, глобальные идентификаторы (т. е. идентификаторы, описанные в вышестоящем модуле) и локальные идентификаторы (т. е. идентификаторы, описанные в данном модуле). Динамически, в некоторый момент времени выполнения программы, ее среда данных состоит из трех множеств: переменные, описанные в программе, но не существующие в памяти, переменные, существующие в памяти, к которым нет доступа и переменные, существующие в памяти, которым есть доступ. Множество переменных, существующих в памяти, к которым нет доступа возникает в случае совпадения глобальных идентификаторов и локальных идентификаторов какой-либо процедуры или функции. При этом в момент работы такой процедуры или функции доступ к объектам, связанным с глобальными идентификаторами будет закрыт. По выходе из процедуры, когда соответствующий локальный идентификатор утрачивает свою силу, доступ к этому объекту возобновляется.
Процедурный тип
В стандартном Паскале процедуры и функции рассматриваются только как части программы, которые можно выполнять с помощью вызова процедуры или функции. В Borland Pascal процедуры и функции трактуются гораздо шире: здесь допускается интерпретация процедур и функций, как объектов, которые можно присваивать переменным и передавать в качестве параметров. Такие действия можно выполнять с помощью процедурных типов. В описании процедурного типа задаются параметры, а для функции - результат функции. Ниже приведен синтаксис описания процедурного типа.
<задание процедурного типа>::= procedure (<список формальных параметров>) | function (<список формальных параметров>): <результат>
Синтаксис записи процедурного типа в точности совпадает с записью заголовка процедуры или функции, только опускается идентификатор после ключевого слова procedure или function. Приведем некоторые примеры описаний процедурного типа:
type
Proc= procedure;
SomeIntProc= procedure(X, Y: Integer; var Z: Integer);
SomeStrProc= procedure(S: String);
SomeMathFunc= function(X: Real): Real;
Borland Pascal не позволяет описывать функции, которые возвращают значения процедурного типа. Результат функции должен быть строкового, вещественного, целого, символьного, булевского типа, указателем или иметь перечислимый тип, определенный пользователем. Переменной процедурного типа можно присвоить одно из следующих значений: значение nil, ссылку на переменную процедурного типа и идентификатор процедуры или функции. В контексте процедурных значений описание процедуры или функции можно рассматривать как специальный вид описаний констант, когда значением константы является процедура или функция. Рассмотрим, например, следующее описание:
var
P: SomeSwapProc;
F: SomeMathFunc;
procedure Max(A, B: Integer; var C: Integer);
begin
if A > B then C:= A
else C:= B;
end;
function Tan(Angle: Real): Real;
begin
Tan:= Sin(Angle) / Cos(Angle);
end;
Переменным P и F можно присвоить значения следующим образом:
P:=Swap;
F:= Tan;
а вызовы с помощью P и F можно выполнить так:
P( I, J ); {эквивалентно Max( I, J )}
X:= F( X ); {эквивалентно X:= Tan( X )}
Использование процедурных переменных, которым в операторе вызова процедуры или функции присваивается значение nil, приводит к ошибке. Значение nil предназначено для указания того, что процедурная переменная не присвоена, и, там где процедурная переменная может получить значение nil, участвующие в этой процедурной переменной вызовы процедур и функций следует подвергать проверке:
if @P <> nil then P( I, J );
Здесь операция @ используется для указания того, что P проверяется, а не вызывается.
Процедурный тип может служит одним из элементов структурного типа, а также использоваться в качестве параметров процедур и функций. Параметры процедурного типа особенно полезны в том случае, когда над множеством процедур или функций нужно выполнить какие-то общие действия.
Опережающее описание
В общем случае, до вызова процедуры или функции, ее необходимо описать. Однако, в некоторых случаях желательно обратиться к процедуре или функции до ее описания. Это реализуется механизмом опережающего описания. Описание процедуры, содержащее вместо блока операторов директиву forward, называется опережающим описанием. В каком-либо месте после этого описания, с помощью определяющего описания, процедура должна определяться. Определяющее описание - это описание, в котором используется тот же идентификатор процедуры, но опущен список формальных параметров и в которое включен блок операторов. Между опережающим и определяющим описанием могут описываться другие процедуры и функции, которые могут обращаться к процедуре с опережающим описанием. Опережающее описание и определяющее описание представляют собой полное описание процедуры. Процедура считается описанной с помощью опережающего описания. Ниже приведен пример данной возможности.
...
procedure ForwardSample( V1, V2: real; var V3: real ); forward;
...
procedure P;
var
a, b, c: real;
begin
...
ForwardSample( a, b, c );
...
end;
...
procedure ForwardSample;
begin
if V1 > V2 then V3:= V1 else result:= V2
end;
...
При таком описании процедура ForwardSample доступна для P, процедуры, которая описана до полного описания ForwardSample.
Перечислим основные правила, которым необходимо следовать при передаче данных процедуре или функции:
1. Имя процедуры или функции не должно совпадать с именами других программных объектов (констант, переменных, процедур и т. п.) и с именами типов.
2. Количество параметров необходимо выбирать с учетом того, что при их увеличении растет гибкость, но ухудшается быстродействие (связанное настройкой на заданные фактические параметры).
3. Идентификаторы для формальных параметров могут быть любыми, из числа не использующихся в данной процедуре для других целей (в том числе и те, которые используются в основной программе, если они не присутствуют в теле процедуры или функции).
С этим положением связано появление коллизии, которую иллюстрирует следующий пример.
var
CollisionVar, Var1, Var2: real;
...
procedure CalcSum( CollisionVar, Var1: real; var Var2: real );
begin
Var2:= CollisionVar + Var1
end;
...
CalcSum( V1, V2, CollisionVar ); {вызов такого вида}
...
{можно интерпретировать следующим образом}
var
CollisionVar, Var1: real;
begin
CollisionVar:= V1;
Var1:= V2;
begin
CollisionVar:= {внешняя переменная}
CollisionVar {внутренняя переменная}
+ V1
end
end
Данная коллизия устраняется системой путем автоматического изменения формальных параметров. Возможность такого решения связана с отведением под параметры-значения отдельной области памяти. Это можно интерпретировать следующим образом.
var
CollisionVar1, Var11: real;
begin
CollisionVar1:= V1;
Var11:= V2;
begin
CollisionVar:= {внешняя переменная}
CollisionVar1 {внутренняя переменная}
+ V11
end
end
4. Порядок перечисления формальных параметров может быть любым, но его нужно зафиксировать.
При вызове процедуры или функции необходимо обратить внимание на следующие положения:
5. Число фактических параметров должно совпадать с количеством формальных.
6. Отождествление фактических и формальных параметров производится слева направо.
7. Тип фактического параметра должен соответствовать типу формального.
8. При передаче по значению фактическим параметром может быть любое выражение соответствующего типа, в то время как при передаче по ссылке фактическим параметром может быть только переменная с типом, соответствующим типу формального параметра.
Разберем решение типичной задачи, связанной с использованием процедур и функций.
Текст задания
По заданным вещественным числам c и d ( c < d ) вычислить
интегралы вычислять приближенно по формуле трапеций при n= 20 для первого интеграла и при n= 100 для второго:
где h= ( b - a )/ n.
Решение
program ntegrals;
type RealFunct= function( x: real ): real;
var
c, d: real;
function f1( x: real ): real;
begin
f1:= sqr( arctan( x ) )
end;
function f2( x: real ): real;
begin
f2:= sin( exp( 10 * x ) )
end;
function int( f: RealFunct; a, b: real; n: integer): real;
var
h, s: real;
i: integer;
begin
h:= ( b - a )/n;
s:= ( f( a ) + f( b ))/2;
for i:= 1 to n-1 do s:= s + f( a + i * h );
int:= h * s
end; {of int}
begin
read( c, d );
writeln( int( f1, c, d, 20 ) +
int( f2, 0, 3.1415927, 100 ) )
end.
Варианты заданий
1. Дано n вещественных чисел(n=100). Упорядочить их по неубыванию методом фон Неймана: завести два массива A и B и записать исходные числа в A; упорядочить пары соседних чисел (A1 и A2 , A3 и A4 и т.д.) и записать их в B; взять из B две соседние упорядоченные пары и, слив их в упорядоченные четверки, снова записать в A; затем каждые две соседние четверки из B слить в упорядоченные восьмерки и перенести в A; и т.д.
2. В точках 1,2,┘,k, где k- заданное целое число от 2 до 70, напечатать (по отдельности) графики следующих функций: f(n) - количество целых чисел от 1до n, взаимно простых с числом n; t(n) - количество положительных делителей числа n ; p(n) - количество простых чисел , не превосходящих n.
3. Даны две целые квадратные матрицы 10-го порядка. Определить, можно ли отражениями относительно главной и побочной диагоналей преобразовать одну из них в другую.
4. Даны натуральное число p и вещественные квадратные матрицы A,B и C 4-го порядка. Получить (ABC) в степени p.
5. Даны коэффициенты многочленов P(x) и Q(x) 15-й степени и дано вещественное число a. Вычислить величину P( a + Q( a ) * P( a + 1 ) ).
6. Два натуральных числа называются "дружественными", если каждое из них равно сумме всех делителей другого , за исключением его самого (таковы ,например ,числа 220 и 284). Напечатать все пары "дружественных" чисел ,не превосходящих заданного натурального числа.
7. Два простых числа называются "близнецами" ,если они отличаются друг от друга на 2 (таковы, например, числа 41 и 43). Напечатать все пары "близнецов" из отрезка [n,2n], где n- заданное целое число больше 2.
8. Найти наименьшее общее кратное четырех заданных натуральных чисел.
9. Три прямые на плоскости заданны уравнениями akx + bky= ck (k=1, 2, 3). Если эти прямые попарно пересекаются и образуют треугольник, тогда найти его площадь.
10. Даны координаты вершин треугольника и координаты некоторой точки внутри него. Найти расстояние от данной точки до ближайшей стороны треугольника. (При определении расстояний учесть, что площадь треугольника вычисляется и через три его стороны, и через основание и высоту.)
11. Даны координаты вершин двух треугольников. Определить, какой из них имеет большую площадь.
12. const d=100; m=5; type position= 1..d; var x: String[d]; y, z: String[m]; Описать логическую функцию search(s,ss,k,n), проверяющую, входит ли подстрока ss в ту часть строки s, которая начинается с k-й позиции, и, если входит, присваивающую параметру n номер позиции, с которой начинается первое вхождение ss в эту часть строки s. Используя данную функцию, заменить в строке x все вхождения подстроки y на подстроку z.
13. Описать функцию next без параметров, которая считывает из входного файла первую литеру, отличную от пробела, и объявляет ее своим значением. Использовать эту функцию для подсчета k- количества отличных от пробела литер текста, который заданы во входном файле и за которым следует точка.
14. const d=20; type вектор=array[1..n] of real; Описать процедуру change(x,y,z), которая в том из векторов x, y и z, где больше всего отрицательных элементов (считать что такой вектор один),все его положительные элементы заменяет: на их кубы - если это вектор x или вектор z, и на их обратные величины - если это вектор y.
15. Напечатать в порядке возрастания корни уравнений 1/(1+x^2)=x, 3e^x+x=0, x*ln(1+x)=0.5 вычисленные с заданной точностью
16. Даны 6-элементные вещественные векторы x и y и квадратные матрицы A, B и C 6-го порядка. Вычислить величину (ах, Bx)+(Cx, y)/(x, By).
17. Даны три вещественные квадратные матрицы 4-го порядка. Напечатать ту из них, норма которой наименьшая (считать, что такая матрица одна). В качестве нормы матрицы взять максимум абсолютных величин ее элементов.
18. Дана непустая последовательность слов, в каждом из которых от 1 до 6 латинских букв; между соседними словами - запятая, за последним словом точка. Напечатать те слова, у которых одинаковые "соседи" т.е. совпадают предыдущее и следующее слова. (Определить процедуру readword(w),которая вводит очередное слово и присваивает его 6-ти литерной строке w, а запятую или точку присваивает некоторой глобальной переменной.)
19. type table1=array[1..10,1..10]of integer; table2=array[1..20,1..30] of integer; Описать процедуру constr (A,B,C,D), которая по матрицам A,B и C типа table1 строит следующую матрицу D типа table2:
( A B C )
D= ( B N A )
где N- нулевая матрица типа table1.
20. const n=┘;{целая константа > 1} type number=array [1..n] of '0'..'9'; matrix= array[1..40] of number; Описать процедуру sort( x ), упорядочивающую по неубыванию числа массива x следующим методом: все числа из x упорядочить по последней цифре и перенести в массив y; затем числа из y упорядочить по предпоследней цифре (при равенстве этих цифр сохранить упорядоченность по последней цифре) и записать их снова в массив x; далее числа из x упорядочить по третьей с конца цифре и перенести в массив y и т.д. (Учесть, что в конце концов числа должны оказаться в x).
21. Даны две квадратные вещественные матрицы 10-го порядка. Напечатать квадрат той из них, в которой наименьший след (сумма диагональных элементов), считая, что такая матрица одна.
22. Даны 30-элементные вещественные векторы x, y и z. Вычислить величину (a, a)-(b, c), где a обозначает тот из этих векторов, в котором самый большой минимальный элемент (считать ,что такой вектор единственный ), b и c обозначают два других вектора, а (p, q)-скалярное произведение p и q .
23. По заданным 50-элементным вещественным массивам a, b и c вычислить
t= min( bi )/max( ai ) + max( ci )/ min( bi + ci ), при min( ai ) < max( bi ),
t= max( bi + ci ) + min( ci ), иначе.
24. По заданным 20-элементным целым массивам x и y вычислить
25. const n=15; m= 20; type matrix= array[1..n,1..m] of real; Описать функцию sum(А), вычисляющую величину x1xn + x2xn-1 +...+ xnx1где xi максимальный элемент i- строки матрицы А.
26. Даны три слова, в каждом из которых от 1 до 6 латинских букв и за каждым из которых следует пробел. Напечатать эти слова в алфавитном прядке.
27. Даны длины а, в и с сторон некоторого треугольника. Найти медианы треугольника, сторонами которого являются медианы исходного треугольника. (Замечание: длина медианы, проведенной к стороне а, равна 0.5 * sqrt( 2b^2 + 2c^2 - a^2 ).)
28. Найти сумму кубов всех корней уравнения ex^3 - PIx^2 - ( 2e + 1 )x + 2PI=0 (PI=3.1415927, e=2.782818), вычисленных с точностью 0.0001.
29. Даны три натуральных числа. Определить их наибольший общий делитель.
30. Даны отрезки a ,b, c и d. Для каждой тройки этих отрезков, из которых можно построить треугольник, напечатать площадь данного треугольника. (Определить процедуру print( x, y, z ), печатающую площадь треугольника со сторонами x , y и z, если такой треугольник существует).