Использование массивов и функций в качестве формального параметра.
Формальным параметром функции может быть не только переменная скалярного типа, но и идентификатор переменной структурированного типа данных.
Использование массива в качестве параметра.
Пример 3. Массивы X(k), Y(m), Z(n) представляют собой три выборки значений случайной величины из общей генеральной совокупности. Требуется для каждой из трех выборок X, Y и Z вычислить их эмпирическую среднюю М и несмещенную дисперсию D.
Для решения этой задачи нужно запрограммировать две групповые пользовательские функции Avg и VarD, которые описываются с помощью формул:
Функции Avg и VarD имеют в качестве параметра имя массива, содержащего значения случайной величины и объем выборки.
Для описания этого примера понадобилось целых три структурограммы, однако они просты, наглядны, компактны и легки для понимания. Для составления текста программы сделаем предположение, что объемы выборок не превосходят целого числа G: G= Sup.
Для передачи имени объекта в качестве параметра этот объект нужно в явном виде описать в разделе TYPE. Мы хотим в качестве параметра передавать имя одномерного массива, поэтому дадим этому типу идентификатор VEK.
PROGRAM PR3;
CONST G = 50;
TYPE VEK = ARRAY [ 1 .. G] OF REAL;
VAR X, Y, Z: VEK; I, К, M, N: INTEGER;
FUNCTION AVG(P: INTEGER; W: VEK): REAL;
VAR J: INTEGER; S: REAL;
BEGIN
S:=0;
FOR J := 1 TO P DO S := S + W[J];
AVG := S/P
END;
FUNCTION VARD(P: INTEGER; W: VEK): REAL;
VAR J: INTEGER; M, S: REAL;
BEGIN
M:= AVG(P, W); S := 0;
FOR J := 1 TO P DO S := S + SQR(W[J] — M);
VARD:=S/(P- 1)
END;
BEGIN
WRITELN(‘Укажите размерность К массива X ‘); READLN(K);
WRITELN(‘Введите массив X, из ‘, К:2,’ вещественных чисел’);
FOR I := 1 ТО К DO READ(X[I]);
WRITELN(‘Укажите размерность M массива Y ‘); READLN(M);
WRITELN(‘Введите массив Y, из ‘,M:2,’ вещественных чисел’);
FOR I := 1 ТО M DO READ(Y[I]);
WRITELN(‘Укажите размерность N массива Z ‘); READLN(N);
WRITELN(‘Введите массив Z, из ‘, N:2,’ вещественных чисел’);
FOR I := 1 ТО N DO READ(Z[I]);
WRITELN(‘M(X) = ‘, AVG(K, X) :10:6,’, D(X) = ‘, VARD(K, X) :10:6);
WRITELN(‘M(Y) = ‘, AVG(M, Y) :10:6,’, D(Y) = ‘, VARD(M, Y) :10:6);
WRITELN(‘M(Z) = ‘, AVG(N, Z) :10:6,’, D(Z) = ‘, VARD(N, Z) :10:6)
END.
Использование функций в качестве параметра.
Идентификатор функции или процедуры может использоваться в качестве параметра другой функции. Если такая необходимость появилась, то требуется соответствующим образом специфицировать каждый такой формальный параметр.
Спецификацией в этом случае являются: заголовок передаваемой функции, включающей имя функции, список формальных параметров и тип результата. Спецификация функции описывается в разделе TYPE следующим образом: TYPE = FUNCTION(): ;
Пример 4. В интервале [А, В] методом поразрядного приближения найти максимум функции
F(X) = 0.1Х 3 – 2Х 2 + 10Х.
Формульно-словесный алгоритм метода состоит в следующем.
1. Задается начальное приближение Х0= А слева от максимума F(X) и вычисляется F(X0). Задается D = h, где h = ?Х — начальный шаг поиска.
2. Полагаем G = F(Xn), где вначале F(Xn) = F(X0), задаем X = X + D и вычисляем F(Xn+1)=F(X).
3. Проверяем условие F(Xn+1) > G; если оно выполняется, то идем к п. 2, если нет – к п. 4.
4. Полагаем D = –D/4. Проверяем условие | D | >Е/4, где Е — заданная погрешность вычисления Хn в точке максимума. Если она выполняется, идем к п. 2, то есть обеспечиваем поиск максимума в другом направлении с шагом в четыре раза меньше прежнего. Если данное условие выполняется, заканчиваем счет, принимая за максимум X.
В этом известном алгоритме не учитывается тот факт, что в заданном интервале [А, В] нет экстремума (максимума). В этом случае максимум будет на границе интервала либо в точке А, либо в точке В. Учитывая простоту функции F(X) и основной программы, приведем структурограмму только функции PRP(A, В, Е, F), которая осуществляет поиск максимума функции F в интервале [А, В] с погрешностью приближения Е.
Структурограмма функции PRP(A, В, Е, F):
Задав значения [А, В] = [2,3], E = 10 -9 , получим X = 3,0; F(X) = 14.7. Очевидно, что экстремума в этом интервале нет, так как Хmax на границе интервала в точке В. Изменив исходные данные: [А, В] = [2, 5], Е = 10 -9 , получим Хmax = 3,333332149; F(Хmax)= 14.814814815.
В теле функции использован оператор EXIT, который обеспечивает нормальный выход из фун-ции в том случае, когда нет экстремума и максимальное значение — на границе интервала.
Замечание. Как правило, компилятор Паскаля автоматически подбирает метод адресации подпрограмм. Если подпрограмма находится в одном тексте с телом основной программы, то она компилируется с «ближним» (near) адресом входа и возврата, содержащим только величину смещения адреса внутри текущего сегмента памяти. Если подпрограмма находится в другом программном модуле, то генерируется «дальний» (far) адрес, содержащий адрес сегмента и смещения в сегменте. При передаче имени функции в качестве программы необходим «дальний» адрес, но компилятор этого не понимает. Поэтому необходимо использовать директиву компилятора . Действие этой директивы распространяется на все подпрограммы, описанные ниже по тексту программы, или до директивы .
Пример 5. Найти сумму
где Ui = F(i). Функция F(i) может быть различной: F1(i) = 1/i; F2(i) = i/(i + 1); F3(i) = Cos (?/i).
При решении этой задачи следует помнить, что передача в качестве параметров предопределенных (стандартных) функций, к которым относится и Cos(X), запрещена. Это ограничение обходится путем переопределения стандартной функции в пользовательскую функцию. Надежный сервис почтовых рассылок выполнит рассылку на электронные почтовые адреса быстро и недорого.
PROGRAM PR5;
TYPE FUN = FUNCTION(A: INTEGER): REAL;
VAR S1: REAL; M, N, K: INTEGER;
FUNCTION F1(I: INTEGER): REAL;
BEGIN Fl := 1/I
END;
FUNCTION F2(I: INTEGER): REAL;
BEGIN F2:= I/(I+1)
END;
FUNCTION F3(I: INTEGER): REAL; < Переопределение функции COS >
BEGIN F3 := COS(PI/I)
END;
FUNCTION SUM(F : FUN): REAL;
VAR I: INTEGER;
S: REAL;
BEGIN
S:=0;
FOR I:=M TO N DO S := S + F(I); SUM:=S END;
BEGIN
WRITELN(‘Задайте M, N и номер функции (1, 2, 3)’);
READ(M, N, К);
CASE К OF
1:S1:= SUM(Fl);
2: Sl:= SUM(F2);
3:S1:=SUM(F3)
END;
WRITELN(‘S = ‘,S1:8:5)
END.
Параметры M и N передаются в функцию SUM в качестве глобальных переменных. Фактическим параметром для этой функции является идентификатор одной из трех функций Fl, F2, F3.
11. Процедуры и функции
Как вы знаете с отдельными элементами массивов можно работать также как с обычными (скалярными) переменными. Соответственно можно использовать их в качестве фактических параметров. Однако, может потребоваться передать процедуре не отдельные элементы, а весь массив целиком. При этом недопустим такой, например, способ описания параметра:
procedure P(a: array [1..10] of integer);
Тип параметра должен быть или скалярным или заранее определенным в разделе type. То есть правильным будет следующее описание:
type TArray = array [1..10] of integer; var x: TArray; procedure P(a: TArray);
Такую процедуру P можно вызывать, указав в качестве фактического параметра, например, глобальную переменную x.
Пример: Программа, вычисляющая сумму элементов массива.
const n = 10; type TArray = array [0..n-1] of integer; var x: TArray; i, sum: integer; procedure Summa(a: TArray; var s:integer); var i: integer; begin s:=0; for i:=0 to n-1 do s:=s+a[i]; end; begin for i:=0 to n-1 do readln(x[i]); Summa(x, sum); writeln(sum); end.
При вызове процедуры содержимое массива x будет скопировано в массив a. Глобальная переменная sum на время работы процедуры приобретет синоним s. То есть, присваивая что-то переменной s, мы тем самым изменяем и переменную sum. В итоге программа выведет сумму элементов введенного пользователем массива.
При передаче массива через параметр-значение происходит копирование его содержимого в новые ячейки памяти. При работе с массивами большого размера или при необходимости вызывать процедуру очень много раз это может привести к большим затратам времени. Чтобы этого избежать, можно передавать массивы через параметры-переменные. При этом фактического переноса содержимого массивов из одной области памяти в другую не происходит, и вызов процедуры занимает меньше времени. То есть при большом размере массива правильнее будет заголовок:
procedure Summa(var a: TMassive; var s: integer);
Динамический или статический?
Границы массива обязательно задаются константами, и изменить размер массива в ходе работы программы нельзя. Зато можно сделать индекс не только целого, но и, скажем, символьного или перечислимого типа. Например, для подсчета встречаемости каждой буквы можно использовать массив
var LettersCount: array ['a'..'z'] of integer;
и работать с ним в свое удовольствие:
LettersCount['z'] := 1; LettersCount['d'] := LettersCount['d'] + 1;
Недостатки таких массивов известны: если заранее неизвестно, сколько элементов потребуется использовать, то под массив отводится память максимального размера. В итоге в большинстве случаев мы «запасаемся впрок», а иногда и этого «запаса» оказывается недостаточно. Именно поэтому такие массивы называются статическими: их размер статичен и должен быть задан на этапе компиляции программы.
В Delphi Object Pascal появились динамические массивы, размер которых можно не только задавать, но и менять по ходу работы программы. Именно об этих массивах и о преимуществах их использования пойдет речь далее.
Описываются они предельно просто:
var a: array of integer;
Чтобы задать размер такого массива, следует вызвать процедуру SetLength:
var n: integer; read(n); SetLength(a,n);
Как мы видим, размер такого массива может быть задан переменной, которая вычисляется по ходу работы програмы (в данном случае ее значение вводится).
Чтобы определить размер динамического массива, следует вызвать функцию Length: Length(a) возвращает размер динамического массива.
Индексы динамического массива — только целочисленные. Кроме того, нижняя граница индекса всегда равна нулю. То есть после вызова SetLength ( a,n ) элементами массива a являются a[0]..a[n-1]. Вот как вывести элементы динамического массива:
for i:=0 to Length(a)-1 do write(a[i],' ');
Чем замечательна процедура SetLength — так это тем, что она может вызываться повторно, и при последующих вызовах старое содержимое массива сохраняется:
SetLength(a,3); a[0] := 666; SetLength(a,5); writeln(a[0]); // выведется 666
Динамические массивы представляются в памяти ссылками. Это означает, что любая переменная типа «динамический массив» является указателем на непрерывный участок динамической памяти. Первоначально этот указатель хранит nil, а вызов SetLength(a) выделяет под данные массива блок динамической памяти и записывает в a адрес этого блока памяти.
Тот факт, что переменные — динамические массивы — это всего лишь адреса, имеет несколько следствий.
Во-первых, при присваивании статических массивов копируется содержимое, а при присваивании динамических — только указатель:
var a1,a2: array [1..10] of integer; var b1,b2: array of integer; a1 := a2; // копируется содержимое b1 := b2; // копируется указатель
То есть, присваивание больших статических массивов происходит долго, а присваивание динамических — быстро независимо от их размера.
Во-вторых, при присваивании динамических массивов обе пременные b1 и b2 указывают на один участок динамической памяти, поэтому изменение элемента в массиве b1 приводит и к изменению массива b2:
b1[0] := 5; writeln(b2[0]); // выведется 5
Чтобы создать копию данных динамического массива, необходимо вызвать функцию Copy:
b2[0] := 3; b1 := Copy(b2); b1[0] := 5; writeln(b2[0]); // выведется 3
Передача динамических массивов в подпрограммы тоже проста:
type IntArr = array of integer; procedure print(a: IntArr); var i: integer; begin for i:=0 to Length(a)-1 do write(a[i],' '); end; var b: IntArr; . print(b);
Передавать динамические массивы по ссылке чтобы исключить копирование массива не имеет никакого смысла — в подпрограмму передается указатель. Динамический массив имеет смысл передавать как var-параметр только в одном случае: если мы отводим в подпрограмме для него память:
procedure CreateAndFill(var a: IntArr; n: integer; fill: integer); var i: integer; begin SetLength(a,n); for i:=0 to n-1 do a[i] := fill; end.
И, наконец, в использовании динамических массивов в подпрограммах скрыта одна западня: если мы меняем элемент массива внутри подпрограммы, то меняется соответствующий массив — фактический параметр:
procedure Trap(a: IntArr); begin a[0] := 666; end; var b: IntArr; . b[0] := 777; Trap(b); writeln(b[0]); // выведется 666
Еще в Delphi имеются так называемые открытые массивы. К сожалению, они по внешнему виду очень похожи на динамические:
procedure print1(a: array of integer); var i: integer; begin for i:=0 to High(a)-1 do write(a[i],' '); end;
Смысл в том, что при вызове на место открытого массива можно подставлять статический массив любого размера. Но запись array of integer используется в совершенно другом смысле! Поэтому мы полностью отказались от открытых массивов в PascalABC.NET. Пользуйтесь динамическими массивами!
Посмотрим теперь, что нового появилось в динамических массивах в PascalABC.NET.
1. Динамические массивы можно инициализировать при описании:
var a: array of integer := (1,3,5);
2. Выделять память под динамическе массивовы можно с помощью операции new:
a := new integer[5];
Такой способ хорош тем, что он подчеркивает, что динамический массив в .NET является классом. Плох же он тем, что при повторном выделении памяти таким способом старое содержимое теряется.
3. Как мы упомянули, динамический массив в PascalABC.NET является классом, а значит, он имеет методы и свойства:
a. Length — свойство, возвращающее длину массива
System. Array . Sort ( a ) — статический метод, сортирующий массив a по возрастанию
System. Array . Reverse ( a ) — статический метод, инвертирующий данные в массиве a
и многие другие.
4. Для динамических массивов в PascalABC.NET имеет место структурная эквивалентность типов (в Delphi — именная). Поэтому следующий код в PascalABC.NET будет работать, а в Delphi вызовет ошибку компиляции:
var b1: array of integer; b2: array of integer; . b1 := b2;
5. Ввиду структурной эквивалентности типов для динамических массивов их можно передавать в подпрограмму следующим образом:
procedure print(a: array of integer); begin for var i:=0 to a.Length-1 do write(a[i],' '); end;
Напомним, что открытые массивы в PascalABC.NET отсутствуют!
6. Для динамических массивов (в отличие от статических) можно использовать цикл foreach (при условии, что мы осуществляем доступ к элементам только на чтение):
foreach x: integer in a do write(x,' ');
И, наконец, скажем несколько слов про двумерные динамические массивы. Они моделируются как массивы массивов.
Следующий код иллюстрирует создание двумерного динамического массива размера m на n:
var с: array of array of integer; m,n: integer; . read(m,n); SetLength(с,m); for var i:=0 to m-1 do SetLength(c[i],n);
Новости
20.05.23. На странице https://pascalabc.net/stepikcourse опубликованы новые курсы по PascalABC.NET от центра олимпиадного программирования DL Club.
08.05.23. Вышла версия PascalABC.NET 3.9.0.beta. Основное — ковариантные параметры обобщений, аргументы по умолчанию при вызове подпрограммы, модуль автоматической проверки LightPT.
22.02.23. Открыта регистрация на конференцию «Использование системы программирования PascalABC. NET в обучении программированию»
Copyright © Ivan Bondarev, Stanislav Mihalkovich 2023 All rights reserved. Custom Design by Youjoomla.com
Случайная программа
// Алгоритм Евклида нахождения НОД
// Уровень сложности: 0
function GCD(a,b: integer): integer;
begin
while b<>0 do
begin
var c := a mod b;
a := b;
b := c;
end;
Result := a;
end;
begin
var a,b: integer;
writeln('Введите a,b: ');
read(a,b);
writeln('НОД = ',GCD(a,b));
end.
Передача динамического массива в функцию
Возможно ли передать в функцию два двумерных динамических массива в FreePascal? По Форумам ползал- ничего толкового не нашел.
var m,n,k:integer; mass1: array of array of real; mass2: array of array of real; begin setlength(mass1,m,n); setlength(mass2,n,k); //Вот этот блок нужно как-то запихать в подпрограмму (задача стоит такая) for i:=0 to m-1 do for j:=0 to k-1 do begin summ:=0; for l:=0 to n-1 do summ:=summ+mass1[i,l]*mass2[l,j]; mass3[i,j]:=summ; end; //конец блока readln(); end.
Отслеживать
задан 10 янв 2014 в 11:43
5 2 2 бронзовых знака
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
type TArrayOfArrayOfReal = array of array of Real; procedure DoSomething(var A, B: TArrayOfArrayOfReal); var I, J, L: Integer; begin for I := 0 to High(A) do for J := 0 to High(B[0]) do . end;
Отслеживать
ответ дан 10 янв 2014 в 11:58
1,767 8 8 серебряных знаков 10 10 бронзовых знаков
Спасибо, попробую!
10 янв 2014 в 12:00
- массивы
- pascal
- freepascal
- Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
lang-pascal
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.11.29.1725
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.