Параметр (компьютерное программирование) - Parameter (computer programming)

В компьютерном программировании , A параметр или формальный аргумент представляет собой особый вид переменные используется в подпрограмме для обозначения одной из частей данных , предоставленных в качестве входных данных для подпрограммы. Эти фрагменты данных являются значениями аргументов (часто называемых фактическими аргументами или фактическими параметрами ), с которыми будет вызываться / запускаться подпрограмма. Упорядоченный список параметров обычно включается в определение подпрограммы , так что каждый раз при вызове подпрограммы оцениваются ее аргументы для этого вызова, а полученные значения могут быть присвоены соответствующим параметрам.

В отличие от аргумента в обычном математическом использовании, аргумент в информатике - это фактическое входное выражение, переданное / предоставленное функции, процедуре или подпрограмме в операторе вызова / вызова, тогда как параметр - это переменная внутри реализации подпрограммы. Например, если кто-то определяет addподпрограмму как def add(x, y): return x + y, то x, yэто параметры, а если она вызывается как add(2, 3), то 2, 3аргументы. Обратите внимание, что переменные (и их выражения) из вызывающего контекста могут быть аргументами: если подпрограмма вызывается как, a = 2; b = 3; add(a, b)то переменные a, b являются аргументами, а не значениями 2, 3 . Дополнительную информацию см. В разделе « Параметры и аргументы ».

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

Пример

Следующая программа на языке программирования C определяет функцию с именем «SalesTax» и одним параметром с именем «цена». Тип цены - "двойная" (т.е. число двойной точности с плавающей запятой ). Тип возвращаемого значения функции также является двойным.

double SalesTax(double price)
{
  return 0.05 * price;
}

После того, как функция определена, ее можно вызывать следующим образом:

SalesTax(10.00);

В этом примере функция была вызвана с аргументом 10.00. Когда это произойдет, цене будет присвоено 10.00, и функция начнет вычислять свой результат. Шаги для получения результата указаны ниже и заключены в {}. 0.05 * priceуказывает, что первое, что нужно сделать, это умножить 0,05 на значение цены, что дает 0,50. returnозначает, что функция выдаст результат 0.05 * price. Следовательно, окончательный результат (без учета возможных ошибок округления, с которыми можно столкнуться при представлении десятичных дробей как двоичных) равен 0,50.

Параметры и аргументы

Термины параметр и аргумент могут иметь разное значение в разных языках программирования. Иногда они используются как синонимы, и контекст используется для различения смысла. Термин параметр (иногда называемый формальным параметром ) часто используется для обозначения переменной, указанной в определении функции, в то время как аргумент (иногда называемый фактическим параметром ) относится к фактическому вводу, предоставленному при вызове функции. Например, если определить функцию как def f(x): ..., то x- параметр, а если она вызывается, a = ...; f(a)то a- аргумент. Параметр - это (несвязанная) переменная, а аргумент может быть литералом, переменной или более сложным выражением, включающим литералы и переменные. В случае вызова по значению в функцию передается значение аргумента - например, f(2)и a = 2; f(a)эквивалентные вызовы - при вызове по ссылке с переменной в качестве аргумента передается ссылка на эту переменную - даже если синтаксис вызова функции может остаться прежним. Спецификация передачи по ссылке или по значению должна быть сделана в объявлении и / или определении функции.

Параметры появляются в определениях процедур; аргументы появляются в вызовах процедур. В определении функции f(x) = x*xпеременная x является параметром; в вызове функции f(2)значение 2 является аргументом функции. Грубо говоря, параметр - это тип, а аргумент - это экземпляр.

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

Напротив, аргументы - это выражения, передаваемые процедуре при ее вызове, обычно одно выражение, соответствующее одному из параметров. В отличие от параметров, которые составляют неизменную часть определения процедуры, аргументы могут изменяться от вызова к вызову. Каждый раз, когда вызывается процедура, часть вызова процедуры, которая определяет аргументы, называется списком аргументов .

Хотя параметры также обычно называют аргументами, аргументы иногда рассматриваются как фактические значения или ссылки, присвоенные переменным параметров, когда подпрограмма вызывается во время выполнения . При обсуждении кода, который вызывает подпрограмму, любые значения или ссылки, переданные в подпрограмму, являются аргументами, а место в коде, где эти значения или ссылки даются, является списком параметров . При обсуждении кода внутри определения подпрограммы переменные в списке параметров подпрограммы являются параметрами, а значения параметров во время выполнения являются аргументами. Например, в C при работе с потоками обычно передается аргумент типа void * и приводится к ожидаемому типу:

void ThreadFunction(void* pThreadArgument)
{
  // Naming the first parameter 'pThreadArgument' is correct, rather than
  // 'pThreadParameter'. At run time the value we use is an argument. As
  // mentioned above, reserve the term parameter for when discussing
  // subroutine definitions.
}

Чтобы лучше понять разницу, рассмотрим следующую функцию, написанную на C :

int Sum(int addend1, int addend2)
{
  return addend1 + addend2;
}

Функция Sum имеет два параметра с именами addend1 и addend2 . Он добавляет значения, переданные в параметры, и возвращает результат вызывающей подпрограмме (используя метод, автоматически предоставляемый компилятором C).

Код, вызывающий функцию Sum, может выглядеть так:

int value1 = 40;
int value2 = 2;
int sum_value = Sum(value1, value2);

Переменные value1 и value2 инициализируются значениями. value1 и value2 в этом контексте являются аргументами функции суммы .

Во время выполнения значения, присвоенные этим переменным, передаются в функцию Sum в качестве аргументов. В функции Sum вычисляются параметры addend1 и addend2 , что дает аргументы 40 и 2 соответственно. Значения аргументов складываются, а результат возвращается вызывающей стороне, где он присваивается переменной sum_value .

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

Альтернативная конвенция в Эйфеле

В методе и языке разработки программного обеспечения Eiffel термины аргумент и параметр имеют различное использование, установленное соглашением. Термин « аргумент» используется исключительно в отношении входных данных подпрограммы, а термин « параметр» используется исключительно при параметризации типов для универсальных классов .

Рассмотрим следующее стандартное определение:

    sum (addend1: INTEGER; addend2: INTEGER): INTEGER
        do
            Result := addend1 + addend2
        end

Подпрограмма sumпринимает два аргумента addend1и addend2, которые называются формальными аргументами подпрограммы . Вызов для sumуказывает фактические аргументы , как показано ниже с помощью value1и value2.

    sum_value: INTEGER
    value1: INTEGER = 40
    value2: INTEGER = 2
                
            sum_value := sum (value1, value2)

Параметры также считаются формальными или фактическими . Формальные универсальные параметры используются в определении универсальных классов. В приведенном ниже примере класс HASH_TABLE объявлен как универсальный класс, который имеет два формальных универсальных параметра, Gпредставляющих интересующие данные и Kпредставляющих хэш-ключ для данных:

class HASH_TABLE [G, K -> HASHABLE] 
            

Когда класс становится клиентом HASH_TABLE, формальные универсальные параметры заменяются фактическими универсальными параметрами в универсальном производном . В следующем объявлении атрибута my_dictionaryдолжен использоваться как словарь на основе символьной строки . Таким образом, данные и ключевые формальные общие параметры заменяются фактическими универсальными параметрами типа STRING.

    my_dictionary: HASH_TABLE [STRING, STRING]

Типы данных

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

В некоторых языках используется специальное ключевое слово (например, void ), чтобы указать, что подпрограмма не имеет параметров; в формальной теории типов такие функции принимают пустой список параметров (тип которого не void , а unit ).

Передача аргумента

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

Аргументы по умолчанию

Некоторые языки программирования, такие как Ada , C ++ , Clojure , Common Lisp , Fortran 90 , Python , Ruby , Tcl и Windows PowerShell, позволяют явно или неявно указывать аргумент по умолчанию в объявлении подпрограммы. Это позволяет вызывающей стороне опустить этот аргумент при вызове подпрограммы. Если аргумент по умолчанию указан явно, то используется это значение, если оно не предоставлено вызывающей стороной. Если аргумент по умолчанию является неявным (иногда с использованием ключевого слова, например Optional ), тогда язык предоставляет хорошо известное значение (например, null , Empty , ноль, пустая строка и т. Д.), Если значение не предоставляется вызывающей стороной. .

Пример PowerShell:

function doc($g = 1.21) {
    "$g gigawatts? $g gigawatts? Great Scott!"
}
PS  > doc
1.21 gigawatts? 1.21 gigawatts? Great Scott!

PS  > doc 88
88 gigawatts? 88 gigawatts? Great Scott!

Аргументы по умолчанию можно рассматривать как частный случай списка аргументов переменной длины.

Списки параметров переменной длины

Некоторые языки позволяют определять подпрограммы, принимающие переменное количество аргументов . Для таких языков подпрограммы должны перебирать список аргументов.

Пример PowerShell:

function marty {
    $args | foreach { "back to the year $_" }
}
PS  > marty 1985
back to the year 1985

PS  > marty 2015 1985 1955
back to the year 2015
back to the year 1985
back to the year 1955

Именованные параметры

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

Пример PowerShell:

function jennifer($adjectiveYoung, $adjectiveOld) {
    "Young Jennifer: I'm $adjectiveYoung!"
    "Old Jennifer: I'm $adjectiveOld!"
}
PS  > jennifer 'fresh' 'experienced'
Young Jennifer: I'm fresh!
Old Jennifer: I'm experienced!

PS  > jennifer -adjectiveOld 'experienced' -adjectiveYoung 'fresh'
Young Jennifer: I'm fresh!
Old Jennifer: I'm experienced!

Несколько параметров на функциональных языках

В лямбда-исчислении каждая функция имеет ровно один параметр. То, что считается функциями с несколькими параметрами, обычно представлено в лямбда-исчислении как функция, которая принимает первый аргумент и возвращает функцию, которая принимает остальные аргументы; это преобразование, известное как каррирование . Некоторые языки программирования, такие как ML и Haskell , следуют этой схеме. В этих языках, каждая функция имеет ровно один параметр, и то , что может выглядеть как определение функции нескольких параметров, фактически синтаксический сахар для определения функции , которая возвращает функцию, и т.д. Функция приложение является левоассоциативным в них языков, а также в лямбда-исчислении, поэтому то, что выглядит как применение функции к нескольким аргументам, правильно оценивается как функция, примененная к первому аргументу, затем получившаяся функция ко второму аргументу и т. д.

Выходные параметры

Выходной параметр , известный также как параметр из или возврата параметр , является параметром , используемым для вывода, а не более обычного использования для ввода. Использование параметров вызова по ссылке или параметров вызова по значению, где значение является ссылкой, поскольку выходные параметры являются идиомой для некоторых языков, особенно C и C ++, в то время как другие языки имеют встроенную поддержку параметров вывода. Языки со встроенной поддержкой выходных параметров включают Ada (см. Подпрограммы Ada ), Fortran (начиная с Fortran 90 ; см. «Намерение» Fortran ), различные процедурные расширения SQL , такие как PL / SQL (см. Функции PL / SQL ) и Transact. -SQL , C # и .NET Framework , Swift и язык сценариев TScript (см. Объявления функций TScript ).

Точнее, можно выделить три типа параметров или режимов параметров : входной параметр s,выходные параметрыивходной / выходной параметр s; они часто обозначаютсяin,outиin outилиinout. Входной аргумент (аргумент входного параметра) должен быть значением, например инициализированной переменной или литералом, и не должен переопределяться или назначаться; выходной аргумент должен быть назначаемой переменной, но его не нужно инициализировать, любое существующее значение недоступно, и ему должно быть присвоено значение; а аргумент ввода / вывода должен быть инициализированной, назначаемой переменной, и при желании ему может быть присвоено значение. Точные требования и исполнение варьируются в зависимости от языка - например, вAda 83выходные параметры могут быть только назначены, но не прочитаны, даже после присвоения (это было удалено вAda 95,чтобы устранить необходимость во вспомогательной переменной-накопителе). Они аналогичны понятиюзначенияв выражении, являющегося r-значением (имеет значение), l-значением (может быть присвоено) или значением r-value / l-value (имеет значение и может быть присвоено ), соответственно, хотя эти термины имеют специализированные значения в C.

В некоторых случаях различаются только ввод и ввод / вывод, при этом вывод рассматривается как конкретное использование ввода / вывода, а в других случаях поддерживаются только ввод и вывод (но не ввод / вывод). Режим по умолчанию зависит от языка: в Fortran 90 ввод / вывод по умолчанию, в C # и расширениях SQL ввод по умолчанию, а в TScript каждый параметр явно указывается как ввод или вывод.

Синтаксически режим параметра обычно указывается ключевым словом в объявлении функции, например, void f(out int x)в C #. Обычно выходные параметры часто помещаются в конец списка параметров, чтобы их четко различать, хотя это не всегда соблюдается. TScript использует другой подход, когда в объявлении функции перечислены входные параметры, затем выходные параметры, разделенные двоеточием (:), и нет типа возврата для самой функции, как в этой функции, которая вычисляет размер текста. фрагмент:

TextExtent(WString text, Font font : Integer width, Integer height)

Режимы параметров - это форма денотационной семантики , излагающая намерение программиста и позволяющая компиляторам вылавливать ошибки и применять оптимизацию - они не обязательно подразумевают операционную семантику (как на самом деле происходит передача параметров). Примечательно, что хотя входные параметры могут быть реализованы вызовом по значению, а параметры вывода и ввода / вывода - вызовом по ссылке - и это простой способ реализовать эти режимы на языках без встроенной поддержки - это не всегда так. реализовано. Это различие подробно обсуждается в Обосновании Ada '83, в котором подчеркивается, что режим параметров абстрагируется от того, какой механизм передачи параметров (по ссылке или путем копирования) фактически реализуется. Например, в то время как в C # входные параметры (по умолчанию, без ключевого слова) передаются по значению, а выходные и входные / выходные параметры ( outи ref) передаются по ссылке, в PL / SQL входные параметры ( IN) передаются по ссылке, а выходные и Параметры ввода / вывода ( OUTи IN OUT) по умолчанию передаются по значению, а результат копируется обратно, но их можно передать по ссылке с помощью NOCOPYподсказки компилятора.

Синтаксически аналогичная конструкция для выходных параметров заключается в присвоении возвращаемого значения переменной с тем же именем, что и функция. Это можно найти в Pascal и Fortran 66 и Fortran 77 , как в этом примере Pascal:

function f(x, y: integer): integer;
begin
    f := x + y;
end;

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

Использовать

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

Например, чтобы вернуть две переменные из функции в C, можно написать:

int width
int height;

F(x, &width, &height);

где x- входной параметр, widthа height- выходные параметры.

Обычный вариант использования в C и связанных языках - обработка исключений , когда функция помещает возвращаемое значение в выходную переменную и возвращает логическое значение, соответствующее успешности выполнения функции. Типичным примером является TryParseметод в .NET, особенно в C #, который преобразует строку в целое число и возвращает результат trueв случае успеха или falseнеудачи. Это имеет следующую подпись:

public static bool TryParse(string s, out int result)

и может использоваться следующим образом:

int result;
if (!Int32.TryParse(s, result)) {
    // exception handling
}

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

Недостатки

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

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

Object obj = G(y, F(x));

при записи с параметрами вывода и ввода / вывода вместо этого становится (поскольку Fэто выходной параметр для параметра Gввода / вывода):

Object obj;
F(x, &obj);
G(y, &obj);

В особом случае функции с одним выходным или входным / выходным параметром и без возвращаемого значения, композиция функции возможна, если выходной или входной / выходной параметр (или в C / C ++, его адрес) также возвращается функцией, в этом случае приведенное выше становится:

Object obj;
G(y, F(x, &obj));

Альтернативы

Существуют различные альтернативы вариантам использования выходных параметров.

Альтернативой для возврата из функции нескольких значений является возврат кортежа . Синтаксически это будет яснее, если можно использовать автоматическую распаковку последовательности и параллельное присваивание , как в Go или Python, например:

def f():
    return 1, 2
a, b = f()

Вместо этого для возврата значения одного из нескольких типов можно использовать помеченное объединение ; наиболее распространенными случаями являются типы, допускающие значение NULL ( типы параметров ), где возвращаемое значение может быть NULL, чтобы указать на сбой. Для обработки исключений можно вернуть тип, допускающий значение NULL, или вызвать исключение. Например, в Python может быть:

result = parse(s)
if result is None:
    # exception handling

или, более идиоматично:

try:
    result = parse(s)
except ParseError:
    # exception handling

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

Обычной альтернативой выходным параметрам в C и родственных языках является возврат единой структуры данных, содержащей все возвращаемые значения. Например, учитывая структуру, инкапсулирующую ширину и высоту, можно написать:

WidthHeight width_and_height = F(x);

В объектно-ориентированных языках вместо использования параметров ввода / вывода часто можно использовать вызов путем совместного использования , передачи ссылки на объект и последующего изменения объекта, но без изменения того, на какой объект ссылается переменная.

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

Примечания

использованная литература