Процедурный параметр - Procedural parameter - Wikipedia

В вычисление, а процедурный параметр это параметр из процедура это сама по себе процедура.

Эта концепция чрезвычайно мощная и универсальная. программирование инструмент, потому что он позволяет программистам изменять определенные шаги библиотечная процедура произвольно сложными способами, без необходимости понимать или изменять код этой процедуры.

Этот инструмент особенно эффективен и удобен для языков, поддерживающих определения локальных функций, Такие как Паскаль и современный Диалект GNU из C. Тем более, когда закрытие функций доступны. Такую же функциональность (и другие) предоставляет объекты в объектно-ориентированный языки программирования, но по значительно более высокой цене.

Процедурные параметры в некоторой степени связаны с концепциями первоклассная функция и анонимная функция, но отличается от них. Эти две концепции больше связаны с тем, как определяются функции, а не с тем, как они используются.

Основная концепция

На большинстве языков, которые предоставляют эту функцию, процедурный параметр ж подпрограммы п можно назвать внутри тела п как если бы это была обычная процедура:

процедура п(ж):    возвращаться ж(6,3) * ж(2,1)

При вызове подпрограммы п, нужно дать ему один аргумент, который должен быть некоторой ранее определенной функцией, совместимой со способом п использует свой параметр ж. Например, если мы определим

процедура плюс(Икс, у):    возвращаться Икс + у

тогда мы можем позвонить п (плюс), и результат будет плюс(6,3) * плюс(2,1) = (6 + 3) * (2 + 1) = 27. С другой стороны, если мы определим

процедура quot(ты, v):    возвращаться ты/v

тогда звонок п (quot) вернусь quot(6,3)*quot(2,1) = (6/3) * (2/1) = 4. Наконец, если мы определим

процедура зло(z)    возвращаться г + 100

тогда звонок п (зло) не будет иметь особого смысла и может быть отмечен как ошибка.

Синтаксические детали

Некоторые языки программирования, которые имеют эту функцию, могут разрешать или требовать полное объявление типа для каждого процедурного параметра. ж, включая количество и тип его аргументов, а также тип его результата, если таковой имеется. Например, на языке программирования C приведенный выше пример можно записать как

int п(int (*ж)(int а, int б)) {    возвращаться ж(6,3) * ж(2,1);}

В принципе, собственно функция actf который передается как аргумент, когда п вызывается, должен быть совместим по типу с объявленным типом параметра процедуры ж. Обычно это означает, что actf и ж должен возвращать результат одного и того же типа, должен иметь одинаковое количество аргументов, а соответствующие аргументы должны иметь один и тот же тип. Однако имена аргументов не обязательно должны быть одинаковыми, как показано плюс и quot примеры выше. Однако некоторые языки программирования могут быть более ограничительными или более либеральными в этом отношении.

Определение объема

В языках, которые допускают процедурные параметры, правила области видимости обычно определяются таким образом, что процедурные параметры выполняются в своей собственной области. Точнее, предположим, что функция actf передается как аргумент в п, как его процедурный параметр ж; и ж затем вызывается изнутри тела п. Пока actf выполняется, он видит среду своего определения.[пример необходим ]

Реализация этих правил области видимости нетривиальна. К тому времени, когда actf наконец выполняется, записи активации где находятся его переменные среды, может быть сколь угодно глубоко в стеке. Это так называемый проблема вниз funarg.

Пример: обычная сортировка вставкой

Понятие процедурного параметра лучше всего пояснить на примерах. Типичное приложение - это следующая общая реализация вставка сортировки алгоритм, который принимает два целочисленных параметра а,б и два процедурных параметра пре, замена:

процедура Isort(а, б, пре, замена):    целое число я, j;    яа;    пока яб делать        jя;        пока j > а и пре(j, j−1) делать            замена(j, j−1);            jj−1;        яя+1;

Эту процедуру можно использовать для сортировки элементов Икс[а] через Икс[б] некоторого массива Икспроизвольных тип в указанном пользователем порядке. Параметры пре и замена должно быть два функции, определяемый клиент, оба принимают два целых числа р, s между а и б. В пре функция должна вернуть истинный тогда и только тогда, когда данные хранятся в Икс[р] должен предшествовать данным, хранящимся в Икс[s], в порядке, определяемом клиентом. В замена функция должна обменивать содержимое Икс[р] и Икс[s] и не вернет результата.

Правильным выбором функций пре и замена, одинаковый Isort Процедура может использоваться для переупорядочивания массивов любого типа данных, хранящихся на любом носителе и организованных в любую структуру данных, которая обеспечивает индексированный доступ к отдельным элементам массива. (Обратите внимание, однако, что есть алгоритмы сортировки которые намного эффективнее сортировки вставкой для больших массивов.)

Сортировка чисел с плавающей запятой

Например, мы можем отсортировать массив z из 20 чисел с плавающей запятой, z[1] через z[20] в порядке возрастания, позвонив Isort (1, 20,zprec,zswap), где функции zprec и zswap определены как

процедура zprec(р, s):    возвращаться (z[р] < z[s]);процедура zswap(р, s):    плавать т;    тz[р];    z[р] ← z[s];    z[s] ← т

Сортировка строк матрицы

В качестве другого примера пусть M быть матрица целых чисел с 10 строками и 20 столбцами, с индексами, начинающимися с 1. Следующий код переупорядочит элементы в каждой строке так, чтобы все четные значения располагались перед всеми нечетными значениями:

целое число япроцедура eoprec(р, s):    возвращаться (M[я, р] мод 2) < (M[я, s] мод 2);процедура eoswap(р, s):    целое число т;    тM[я,р];    M[я,р] ← M[я,s];    M[я,s] ← т;за я с 1 к 10 делать    Isort(1, 20, eoprec, eoswap);

Обратите внимание, что эффекты eoprec и eoswap зависит от номера строки я, но Isort процедура не должна знать этого.

Процедура векторной сортировки

В следующем примере используется Isort определить процедуру вектор это принимает целое число п и целочисленный вектор v с элементами v[0] через v[п−1] и сортирует их в порядке возрастания или убывания, в зависимости от того, используется ли третий параметр incr является истинный или же ложный, соответственно:

процедура вектор(п, v, incr):    процедура vprec(р, s):        если incr тогда            возвращаться v[р] < v[s];        еще            возвращаться v[р] > v[s];    процедура vswap(р, s):        целое число т;        тv[р];        v[р] ← v[s];        v[s] ← т    Isort(0, п−1, vprec, vswap);

Обратите внимание на использование вложенных определений функций для получения функции vprec эффект которого зависит от параметра incr перешел к вектор. В языках, которые не допускают определения вложенных функций, таких как стандартный C, получение этого эффекта потребует довольно сложных и / или небезопасный код.


Пример: слияние двух последовательностей

В следующем примере показано использование процедурных параметров для обработки абстрактных структур данных независимо от их конкретной реализации. Проблема состоит в том, чтобы объединить две упорядоченные последовательности записей в единую отсортированную последовательность, при этом характер записей и критерий упорядочения могут быть выбраны клиентом. Следующая реализация предполагает только то, что на каждую запись можно ссылаться по адресу памяти, и что существует «нулевой адрес» Λ, который не является адресом какой-либо действительной записи. Клиент должен предоставить адреса А, B первых записей в каждой последовательности и функции пре, следующий, и добавить, что будет описано позже.

процедура слияние(А, B, пре, следующийА, appendA, следующийB, appendB):    адрес ini, плавник, т    ini ← Λ; плавник ← Λ пока А ≠ Λ или B ≠ Λ делать        если B = Λ или же (А ≠ Λ и B ≠ Λ и пре(А, B)) тогда            тследующийА(А)            плавник ← appendA (А, плавник); если ini = Λ тогда iniплавник            Ат        еще            тследующийB(B)            плавникappendB(B, плавник); если ini = Λ тогда iniплавник            Bт    возвращаться ini

Функция пре должен взять адреса р, s из двух записей, по одной из каждой последовательности, и вернуть истинный если первая запись должна предшествовать другой в выходной последовательности. Функция следующийА должен взять адрес записи из первой последовательности и вернуть адрес следующей записи в той же последовательности или Λ, если ее нет. Функция appendA должен добавить первую запись из последовательности А к выходной последовательности; его аргументы - адрес А записи, которую нужно приложить, и адрес плавник последней записи выходного списка (или Λ, если этот список все еще пуст). Процедура appendA должен вернуть обновленный адрес последнего элемента списка вывода. Процедуры следующийB и appendB аналогичны другой входной последовательности.

Объединение связанных списков

Чтобы проиллюстрировать использование общей процедуры слияния, вот код для слияния двух простых связанные списки, начиная с узлов по адресам р, S. Здесь мы предполагаем, что каждая запись Икс содержит целочисленное поле Икс.ИНФОРМАЦИЯ и поле адреса Икс.СЛЕДУЮЩИЙ это указывает на следующий узел; где Информация поля в каждом списке расположены в порядке возрастания. Списки ввода разбираются слиянием, и их узлы используются для построения списка вывода.

процедура listmerge(р, S):    процедура пре(р, s):        возвращаться р.ИНФОРМАЦИЯ < s.ИНФОРМАЦИЯ    процедура следующий(Икс):        возвращаться Икс.СЛЕДУЮЩИЙ    процедура добавить(Икс, плавник)        если плавник ≠ Λ тогда плавник.СЛЕДУЮЩИЙИкс        Икс.СЛЕДУЮЩИЙ ← Λ возвращаться Икс         возвращаться слияние(р, S, пре, следующий, добавить, следующий, добавить)

Слияние векторов

Следующий код демонстрирует независимость универсального слияние процедура от фактического представления последовательностей. Он объединяет элементы двух обычных массивов U[0] через U[м−1] и V[0] через V[п−1] чисел с плавающей запятой в порядке убывания. Входные массивы не изменяются, а объединенная последовательность значений сохраняется в третьем векторе. W[0] через W[м+п−1]. Как и в языке программирования C, мы предполагаем, что выражение "&V"дает адрес переменной V, "*п"дает переменную, адрес которой является значением п, и что "& (Икс[я])" эквивалентно "&(Икс[0]) + я"для любого массива Икс и любое целое число я.

процедура arraymerge(U, м, V, п, W):    процедура пре(р, s):        возвращаться (*р) > (*s)    процедура следующий(Икс):        если Икс = &(U[м−1]) тогда возвращаться Λ еще возвращаться Икс + 1    процедура следующийV(Икс):        если Икс = &(V[п−1]) тогда возвращаться Λ еще возвращаться Икс + 1    процедура добавить(Икс, плавник)        если плавник = Λ тогда плавник ← &(W[0])        (*плавник) ← (*Икс)        возвращаться плавник + 1            если м = 0, тогда U ← Λ если п = 0, тогда V ← Λ возвращаться слияние(U, V, пре, следующий, добавить, следующийV, добавить)

Пример: определенный интеграл

Интегрирование по интервалу

Следующая процедура вычисляет приблизительное интеграл ж (Икс) dИкс данной реальной стоимости функция ж на заданном интервале [а,б] из реальная линия. В численный метод используется правило трапеции с заданным номером п ступеней; действительные числа аппроксимируются числами с плавающей запятой.

процедура Intg(ж, а, б, п):    плавать т, Икс, s; целое число я    если б = а тогда возвращаться 0    Икса; sж(а) / 2;    за я из 1 к п−1 делать        тя/(п+1); Икс ← (1−т) * а + т * б;        ss + ж(Икс)    sж(б) / 2    возвращаться (ба) * s / п

Интеграция по диску

Рассмотрим теперь проблему интегрирования заданной функции грамм, с двумя аргументами, над диском D с заданным центром (xc,yc) и заданный радиус р. Эта задача может быть сведена к двум вложенным интегралам с одной переменной заменой переменных

Следующий код реализует формула правой части:

процедура DiskIntg(грамм, xc, yc, р, п)    процедура скрежетать(z):        процедура гполярный(т):            плавать Икс, у            Иксxc + z * потому что(т)            уyc + z * грех(т)            возвращаться грамм(Икс, у)        целое число мкруглый(п*z/р)        возвращаться z * Intg(гполярный, 0, 2 * π, м)    возвращаться Intg(скрежетать, 0, р, п)

Этот код использует процедуру интеграции Intg в двух уровнях. Внешний уровень (последняя строка) использует Intg вычислить интеграл от скрежетать(z) за z от 0 до р. Внутренний уровень (предпоследняя строка) определяет скрежетать(z) как линейный интеграл из грамм(Икс,у) над кругом с центром (xc,yc) и радиус z.

История

Процедурные параметры были изобретены еще до появления электронных компьютеров математик Церковь Алонсо, как часть его лямбда-исчисление модель вычисления.

Процедурные параметры как функция языка программирования были введены АЛГОЛ 60. Фактически, АЛГОЛ 60 имел мощный "позвонить по имени "механизм передачи параметров, который может упростить некоторые способы использования процедурных параметров; см. Устройство Дженсена.

Процедурные параметры были важной особенностью Язык программирования LISP, который также ввел понятие закрытия функции или Funarg. В Язык программирования C позволяет указатели на функции передаваться как параметры, которые достигают той же цели и часто используются как обратные вызовы в событийно-ориентированное программирование и как обработчики ошибок. Однако только несколько современных компиляторов C допускают определения вложенных функций, поэтому другие способы его использования относительно редки. Процедурные параметры также были предоставлены в Паскале вместе с определениями вложенных процедур; однако, поскольку стандартный Паскаль не допускал раздельной компиляции, эта функция также мало использовалась в этом языке.

Смотрите также