Parâmetro (programação de computador) - Parameter (computer programming)

Em programação de computador , um parâmetro ou um argumento formal é um tipo especial de variável usado em uma sub - rotina para se referir a um dos dados fornecidos como entrada para a sub-rotina. Essas partes de dados são os valores dos argumentos (freqüentemente chamados de argumentos reais ou parâmetros reais ) com os quais a sub-rotina será chamada / invocada. Uma lista ordenada de parâmetros é geralmente incluída na definição de uma sub-rotina , de modo que, cada vez que a sub-rotina é chamada, seus argumentos para essa chamada são avaliados e os valores resultantes podem ser atribuídos aos parâmetros correspondentes.

Ao contrário do argumento no uso matemático usual, o argumento na ciência da computação é a expressão de entrada real passada / fornecida a uma função, procedimento ou rotina na instrução de invocação / chamada, enquanto o parâmetro é a variável dentro da implementação da sub-rotina. Por exemplo, se alguém define a addsub - rotina como def add(x, y): return x + y, então x, ysão os parâmetros, enquanto se for chamada como add(2, 3), então 2, 3são os argumentos. Observe que as variáveis ​​(e suas expressões) do contexto de chamada podem ser argumentos: se a sub-rotina for chamada como a = 2; b = 3; add(a, b)então as variáveis a, b são os argumentos, não os valores 2, 3 . Consulte a seção Parâmetros e argumentos para obter mais informações.

A semântica de como os parâmetros podem ser declarados e como os (valores de) argumentos são passados ​​para os parâmetros das sub-rotinas são definidos pela estratégia de avaliação da linguagem, e os detalhes de como isso é representado em qualquer sistema de computador particular dependem da chamada convenção desse sistema. No caso mais comum, chamada por valor , um parâmetro atua dentro da sub-rotina como uma nova variável local inicializada com o valor do argumento (uma cópia local (isolada) do argumento se o argumento for uma variável), mas em outros casos , por exemplo, chamada por referência , a variável de argumento fornecida pelo chamador pode ser afetada por ações dentro da sub-rotina chamada.

Exemplo

O programa a seguir na linguagem de programação C define uma função chamada "SalesTax" e tem um parâmetro chamado "preço". O tipo de preço é "duplo" (ou seja, um número de ponto flutuante de precisão dupla ). O tipo de retorno da função também é duplo.

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

Depois que a função for definida, ela pode ser chamada da seguinte maneira:

SalesTax(10.00);

Neste exemplo, a função foi chamada com o argumento 10.00. Quando isso acontecer, 10,00 será atribuído ao preço, e a função começa a calcular seu resultado. As etapas para produzir o resultado são especificadas a seguir, entre {}. 0.05 * priceindica que a primeira coisa a fazer é multiplicar 0,05 pelo valor do preço, o que dá 0,50. returnsignifica que a função produzirá o resultado de 0.05 * price. Portanto, o resultado final (ignorando os possíveis erros de arredondamento encontrados ao representar frações decimais como frações binárias) é 0,50.

Parâmetros e argumentos

Os termos parâmetro e argumento podem ter significados diferentes em diferentes linguagens de programação. Às vezes, eles são usados ​​de forma intercambiável, e o contexto é usado para distinguir o significado. O termo parâmetro (às vezes chamado de parâmetro formal ) é frequentemente usado para se referir à variável conforme encontrada na definição da função, enquanto argumento (às vezes chamado de parâmetro real ) se refere à entrada real fornecida na chamada da função. Por exemplo, se definirmos uma função como def f(x): ..., then xé o parâmetro e, se for chamada por a = ...; f(a)then, aserá o argumento. Um parâmetro é uma variável (não associada), enquanto o argumento pode ser um literal ou variável ou uma expressão mais complexa envolvendo literais e variáveis. No caso de chamada por valor, o que é passado para a função é o valor do argumento - por exemplo, f(2)e a = 2; f(a)são chamadas equivalentes - enquanto na chamada por referência, com uma variável como argumento, o que é passado é uma referência a essa variável - mesmo que a sintaxe para a chamada de função possa permanecer a mesma. A especificação para passagem por referência ou passagem por valor seria feita na declaração e / ou definição da função.

Os parâmetros aparecem nas definições do procedimento; argumentos aparecem em chamadas de procedimento. Na definição da função, f(x) = x*xa variável x é um parâmetro; na chamada de função, f(2)o valor 2 é o argumento da função. Vagamente, um parâmetro é um tipo e um argumento é uma instância.

Um parâmetro é uma propriedade intrínseca do procedimento, incluída em sua definição. Por exemplo, em muitas linguagens, um procedimento para somar dois inteiros fornecidos e calcular a soma precisaria de dois parâmetros, um para cada inteiro. Em geral, um procedimento pode ser definido com qualquer número de parâmetros ou nenhum parâmetro. Se um procedimento tiver parâmetros, a parte de sua definição que especifica os parâmetros é chamada de lista de parâmetros .

Em contraste, os argumentos são as expressões fornecidas ao procedimento quando ele é chamado, geralmente uma expressão que corresponde a um dos parâmetros. Ao contrário dos parâmetros, que formam uma parte imutável da definição do procedimento, os argumentos podem variar de chamada para chamada. Cada vez que um procedimento é chamado, a parte da chamada do procedimento que especifica os argumentos é chamada de lista de argumentos .

Embora os parâmetros também sejam comumente referidos como argumentos, os argumentos às vezes são considerados os valores reais ou referências atribuídas às variáveis ​​de parâmetro quando a sub-rotina é chamada em tempo de execução . Ao discutir o código que está chamando uma sub-rotina, quaisquer valores ou referências passados ​​para a sub-rotina são os argumentos, e o local no código onde esses valores ou referências são fornecidos é a lista de parâmetros . Ao discutir o código dentro da definição da sub-rotina, as variáveis ​​na lista de parâmetros da sub-rotina são os parâmetros, enquanto os valores dos parâmetros em tempo de execução são os argumentos. Por exemplo, em C, ao lidar com threads, é comum passar um argumento do tipo void * e convertê-lo em um tipo esperado:

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.
}

Para entender melhor a diferença, considere a seguinte função escrita em C :

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

A função Sum possui dois parâmetros, chamados addend1 e addend2 . Ele adiciona os valores passados ​​aos parâmetros e retorna o resultado ao chamador da sub-rotina (usando uma técnica fornecida automaticamente pelo compilador C).

O código que chama a função Sum pode ter a seguinte aparência:

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

As variáveis valor1 e valor2 são inicializadas com valores. valor1 e valor2 são ambos argumentos para a função de soma neste contexto.

Em tempo de execução, os valores atribuídos a essas variáveis ​​são passados ​​para a função Sum como argumentos. Na função Soma , os parâmetros addend1 e addend2 são avaliados, gerando os argumentos 40 e 2, respectivamente. Os valores dos argumentos são adicionados e o resultado é retornado ao chamador, onde é atribuído à variável sum_value .

Por causa da diferença entre parâmetros e argumentos, é possível fornecer argumentos inadequados para um procedimento. A chamada pode fornecer muitos ou poucos argumentos; um ou mais dos argumentos podem ser do tipo errado; ou os argumentos podem ser fornecidos na ordem errada. Qualquer uma dessas situações causa uma incompatibilidade entre as listas de parâmetros e argumentos, e o procedimento frequentemente retornará uma resposta não intencional ou gerará um erro de tempo de execução .

Convenção alternativa em Eiffel

No método e na linguagem de desenvolvimento de software Eiffel , os termos argumento e parâmetro têm usos distintos estabelecidos por convenção. O termo argumento é usado exclusivamente em referência às entradas de uma rotina, e o termo parâmetro é usado exclusivamente na parametrização de tipo para classes genéricas .

Considere a seguinte definição de rotina:

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

A rotina sumrecebe dois argumentos addend1e addend2, que são chamados de argumentos formais da rotina . Uma chamada para sumespecifica os argumentos reais , conforme mostrado abaixo com value1e value2.

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

Os parâmetros também são considerados formais ou reais . Parâmetros genéricos formais são usados ​​na definição de classes genéricas. No exemplo abaixo, a classe HASH_TABLE é declarada como uma classe genérica que possui dois parâmetros genéricos formais, Grepresentando dados de interesse e Krepresentando a chave hash para os dados:

class HASH_TABLE [G, K -> HASHABLE] 
            

Quando uma classe se torna um cliente para HASH_TABLE, os parâmetros genéricos formais são substituídos por parâmetros genéricos reais em uma derivação genérica . Na seguinte declaração de atributo, my_dictionarydeve ser usado como um dicionário baseado em cadeia de caracteres . Como tal, os dados e os parâmetros genéricos formais chave são substituídos por parâmetros genéricos reais do tipo STRING.

    my_dictionary: HASH_TABLE [STRING, STRING]

Tipos de dados

Em linguagens de programação fortemente tipadas , o tipo de cada parâmetro deve ser especificado na declaração do procedimento. As linguagens que usam a inferência de tipo tentam descobrir os tipos automaticamente a partir do corpo e do uso da função. Linguagens de programação digitadas dinamicamente adiam a resolução de tipo até o tempo de execução. Linguagens mal digitadas realizam pouca ou nenhuma resolução de tipo, confiando no programador para correção.

Alguns idiomas usam uma palavra-chave especial (por exemplo, void ) para indicar que a sub-rotina não tem parâmetros; na teoria de tipo formal , tais funções usam uma lista de parâmetros vazia (cujo tipo não é nulo , mas sim unitário ).

Passagem de argumento

O mecanismo exato para atribuir argumentos a parâmetros, chamado passagem de argumento , depende da estratégia de avaliação usada para aquele parâmetro (normalmente chamada por valor ), que pode ser especificada usando palavras-chave.

Argumentos padrão

Algumas linguagens de programação como Ada , C ++ , Clojure , Common Lisp , Fortran 90 , Python , Ruby , Tcl e Windows PowerShell permitem que um argumento padrão seja explicitamente ou implicitamente fornecido na declaração de uma sub-rotina. Isso permite que o chamador omita esse argumento ao chamar a sub-rotina. Se o argumento padrão for fornecido explicitamente, esse valor será usado se não for fornecido pelo chamador. Se o argumento padrão estiver implícito (às vezes usando uma palavra-chave como Opcional ), a linguagem fornece um valor conhecido (como nulo , vazio , zero, uma string vazia, etc.) se um valor não for fornecido pelo chamador .

Exemplo de 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!

Os argumentos padrão podem ser vistos como um caso especial da lista de argumentos de comprimento variável.

Listas de parâmetros de comprimento variável

Algumas linguagens permitem que sub-rotinas sejam definidas para aceitar um número variável de argumentos . Para tais linguagens, as sub-rotinas devem iterar através da lista de argumentos.

Exemplo de 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

Parâmetros nomeados

Algumas linguagens de programação - como Ada e Windows PowerShell - permitem que sub-rotinas tenham parâmetros nomeados . Isso permite que o código de chamada seja mais autodocumentado . Também fornece mais flexibilidade ao chamador, geralmente permitindo que a ordem dos argumentos seja alterada ou que os argumentos sejam omitidos conforme necessário.

Exemplo de 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!

Vários parâmetros em linguagens funcionais

No cálculo lambda , cada função tem exatamente um parâmetro. O que é pensado como funções com vários parâmetros é geralmente representado no cálculo lambda como uma função que recebe o primeiro argumento e retorna uma função que recebe o restante dos argumentos; esta é uma transformação conhecida como currying . Algumas linguagens de programação, como ML e Haskell , seguem esse esquema. Nessas linguagens, cada função tem exatamente um parâmetro, e o que pode parecer a definição de uma função de vários parâmetros, é na verdade um açúcar sintático para a definição de uma função que retorna uma função, etc. A aplicação da função é associativa à esquerda nestes linguagens, bem como no cálculo lambda, então o que parece ser uma aplicação de uma função a vários argumentos é avaliada corretamente como a função aplicada ao primeiro argumento, em seguida, a função resultante aplicada ao segundo argumento, etc.

Parâmetros de saída

Um parâmetro de saída , também conhecido como parâmetro de saída ou parâmetro de retorno , é um parâmetro usado para saída, em vez do uso mais comum para entrada. Usando parâmetros de chamada por referência ou parâmetros de chamada por valor em que o valor é uma referência, pois os parâmetros de saída são uma expressão idiomática em algumas linguagens, notadamente C e C ++, enquanto outras linguagens têm suporte integrado para parâmetros de saída. As linguagens com suporte integrado para parâmetros de saída incluem Ada (consulte subprogramas Ada ), Fortran (desde Fortran 90 ; consulte Fortran "intenção" ), várias extensões de procedimento para SQL , como PL / SQL (consulte funções PL / SQL ) e Transact -SQL , C # e .NET Framework , Swift e a linguagem de script TScript (consulte Declarações de função TScript ).

Mais precisamente, pode-se distinguir três tipos de parâmetros ou modos de parâmetros : parâmetro de entrada s,parâmetros de saídaeparâmetro de entrada / saída s; estas são muitas vezes denotadoin,outein outouinout. Um argumento de entrada (o argumento para um parâmetro de entrada) deve ser um valor, como uma variável inicializada ou literal, e não deve ser redefinido ou atribuído a; um argumento de saída deve ser uma variável atribuível, mas não precisa ser inicializado, qualquer valor existente não está acessível e deve ser atribuído um valor; e um argumento de entrada / saída deve ser uma variável inicializada e atribuível e, opcionalmente, pode ser atribuído um valor. Os requisitos exatos e a aplicação variam entre as linguagens - por exemplo, emAda 83,os parâmetros de saída só podem ser atribuídos, não lidos, mesmo após a atribuição (isso foi removido emAda 95para remover a necessidade de uma variável de acumulador auxiliar). Estes são análogos à noção de umvalorem uma expressão sendo um valor r (tem um valor), um valor l (pode ser atribuído) ou um valor r / valor l (tem um valor e pode ser atribuído ), respectivamente, embora esses termos tenham significados especializados em C.

Em alguns casos, apenas a entrada e a entrada / saída são diferenciadas, com a saída sendo considerada um uso específico de entrada / saída e, em outros casos, apenas a entrada e a saída (mas não a entrada / saída) são suportadas. O modo padrão varia entre as linguagens: em Fortran 90, entrada / saída é o padrão, enquanto em C # e extensões SQL a entrada é padrão e em TScript cada parâmetro é explicitamente especificado como entrada ou saída.

Sintaticamente, o modo de parâmetro geralmente é indicado com uma palavra-chave na declaração da função, como void f(out int x)em C #. Convencionalmente, os parâmetros de saída são frequentemente colocados no final da lista de parâmetros para distingui-los claramente, embora isso nem sempre seja seguido. O TScript usa uma abordagem diferente, onde na declaração de função os parâmetros de entrada são listados, depois os parâmetros de saída, separados por dois pontos (:) e não há tipo de retorno para a função em si, como nesta função, que calcula o tamanho de um texto fragmento:

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

Os modos de parâmetro são uma forma de semântica denotacional , declarando a intenção do programador e permitindo que os compiladores detectem erros e apliquem otimizações - eles não implicam necessariamente em semântica operacional (como a passagem de parâmetro realmente ocorre). Notavelmente, embora os parâmetros de entrada possam ser implementados por chamada por valor, e parâmetros de saída e entrada / saída por chamada por referência - e esta é uma maneira direta de implementar esses modos em linguagens sem suporte integrado - nem sempre é assim que eles são implementado. Esta distinção é discutida em detalhes no Ada '83 Rationale, que enfatiza que o modo de parâmetro é abstraído de qual mecanismo de passagem de parâmetro (por referência ou por cópia) é realmente implementado. Por exemplo, enquanto em C # os parâmetros de entrada (padrão, nenhuma palavra-chave) são passados ​​por valor e os parâmetros de saída e entrada / saída ( oute ref) são passados ​​por referência, em PL / SQL os parâmetros de entrada ( IN) são passados ​​por referência e saída e os parâmetros de entrada / saída ( OUTe IN OUT) são, por padrão, passados ​​por valor e o resultado copiado de volta, mas podem ser passados ​​por referência usando a NOCOPYdica do compilador.

Uma construção sintaticamente semelhante aos parâmetros de saída é atribuir o valor de retorno a uma variável com o mesmo nome da função. Isso é encontrado em Pascal e Fortran 66 e Fortran 77 , como neste exemplo Pascal:

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

Isso é semanticamente diferente porque, quando chamada, a função é simplesmente avaliada - não é passada uma variável do escopo de chamada para armazenar a saída.

Usar

O uso principal dos parâmetros de saída é retornar vários valores de uma função, enquanto o uso de parâmetros de entrada / saída é modificar o estado usando a passagem de parâmetros (em vez de por ambiente compartilhado, como em variáveis ​​globais). Um uso importante de retornar vários valores é resolver o problema do semipredicado de retornar um valor e um status de erro - consulte Problema semipredicado: Retorno com vários valores .

Por exemplo, para retornar duas variáveis ​​de uma função em C, pode-se escrever:

int width
int height;

F(x, &width, &height);

onde xé um parâmetro de entrada e widthe heightsão parâmetros de saída.

Um caso de uso comum em C e linguagens relacionadas é para tratamento de exceções , onde uma função coloca o valor de retorno em uma variável de saída e retorna um booleano correspondente ao sucesso ou não da função. Um exemplo arquetípico é o TryParsemétodo em .NET, especialmente C #, que analisa uma string em um inteiro, retornando trueem caso de sucesso e falsefalha. Este possui a seguinte assinatura:

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

e pode ser usado da seguinte forma:

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

Considerações semelhantes se aplicam ao retorno de um valor de um dos vários tipos possíveis, onde o valor de retorno pode especificar o tipo e, em seguida, o valor é armazenado em uma das várias variáveis ​​de saída.

Desvantagens

Os parâmetros de saída são frequentemente desencorajados na programação moderna, essencialmente por serem estranhos, confusos e de nível muito baixo - valores de retorno comuns são consideravelmente mais fáceis de entender e trabalhar. Notavelmente, os parâmetros de saída envolvem funções com efeitos colaterais (modificação do parâmetro de saída) e são semanticamente semelhantes às referências, que são mais confusas do que funções e valores puros, e a distinção entre parâmetros de saída e parâmetros de entrada / saída pode ser sutil. Além disso, uma vez que em estilos de programação comuns, a maioria dos parâmetros são simplesmente parâmetros de entrada, os parâmetros de saída e os parâmetros de entrada / saída são incomuns e, portanto, suscetíveis a mal-entendidos.

Os parâmetros de saída e entrada / saída impedem a composição da função , uma vez que a saída é armazenada em variáveis, em vez de no valor de uma expressão. Portanto, deve-se inicialmente declarar uma variável e, em seguida, cada etapa de uma cadeia de funções deve ser uma declaração separada. Por exemplo, em C ++ a seguinte composição de função:

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

quando escrito com parâmetros de saída e entrada / saída torna-se ( Fpois é um parâmetro de saída, para Gum parâmetro de entrada / saída):

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

No caso especial de uma função com uma única saída ou parâmetro de entrada / saída e nenhum valor de retorno, a composição da função é possível se o parâmetro de saída ou entrada / saída (ou em C / C ++, seu endereço) também for retornado pela função, nesse caso, o acima se torna:

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

Alternativas

Existem várias alternativas para os casos de uso de parâmetros de saída.

Para retornar vários valores de uma função, uma alternativa é retornar uma tupla . Sintaticamente, isso fica mais claro se o desempacotamento automático da sequência e a atribuição paralela puderem ser usados, como em Go ou Python, como:

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

Para retornar um valor de um dos vários tipos, uma união marcada pode ser usada em seu lugar; os casos mais comuns são tipos anuláveis ( tipos de opção ), onde o valor de retorno pode ser nulo para indicar falha. Para tratamento de exceção, pode-se retornar um tipo anulável ou gerar uma exceção. Por exemplo, em Python, pode-se ter:

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

ou, mais linguisticamente:

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

A micro-otimização de não exigir uma variável local e copiar o retorno ao usar variáveis ​​de saída também pode ser aplicada a funções convencionais e valores de retorno por compiladores suficientemente sofisticados.

A alternativa usual para parâmetros de saída em C e linguagens relacionadas é retornar uma única estrutura de dados contendo todos os valores de retorno. Por exemplo, dada uma estrutura que encapsula largura e altura, pode-se escrever:

WidthHeight width_and_height = F(x);

Em linguagens orientadas a objetos, em vez de usar parâmetros de entrada / saída, pode-se frequentemente usar chamada por compartilhamento , passando uma referência a um objeto e, em seguida, alterando o objeto, embora não alterando a qual objeto a variável se refere.

Veja também

Notas

Referências