Tarefa (ciência da computação)

Na programação de computadores , uma instrução de atribuição define e/ou redefine o valor armazenado no(s) local(is) de armazenamento indicado(s) por um nome de variável ; em outras palavras, copia um valor na variável. Na maioria das linguagens de programação imperativas , a instrução (ou expressão) de atribuição é uma construção fundamental.

Hoje, a notação mais comumente usada para esta operação é (originalmente Superplan 1949–51, popularizada por Fortran 1957 e C ). A segunda notação mais comumente usada é [1] (originalmente ALGOL 1958, popularizada por Pascal ). [2] Muitas outras notações também estão em uso. Em algumas línguas, o símbolo utilizado é considerado um operador (o que significa que a instrução de atribuição como um todo retorna um valor). Outras linguagens definem atribuição como uma declaração (o que significa que não pode ser usada em uma expressão). x = expr x := expr

As atribuições normalmente permitem que uma variável mantenha valores diferentes em momentos diferentes durante sua vida útil e escopo . No entanto, algumas linguagens (principalmente linguagens estritamente funcionais ) não permitem esse tipo de reatribuição "destrutiva", pois pode implicar mudanças de estado não local. O objetivo é impor transparência referencial , ou seja, funções que não dependem do estado de algumas variáveis, mas produzem os mesmos resultados para um determinado conjunto de entradas paramétricas em qualquer momento. Programas modernos em outras linguagens também costumam utilizar estratégias semelhantes, embora menos rigorosas, e apenas em certas partes, a fim de reduzir a complexidade, normalmente em conjunto com metodologias complementares como estruturação de dados , programação estruturada e orientação a objetos .

Semântica

Uma operação de atribuição é um processo de programação imperativa no qual valores diferentes são associados a um nome de variável específico com o passar do tempo. [1] O programa, nesse modelo, opera alterando seu estado por meio de instruções de atribuição sucessivas. [2] [3] Primitivos de linguagens de programação imperativas dependem de atribuição para fazer iteração . [4] No nível mais baixo, a atribuição é implementada usando operações de máquina como MOVEou STORE. [2] [4]

Variáveis ​​são contêineres para valores. É possível colocar um valor em uma variável e posteriormente substituí-lo por um novo. Uma operação de atribuição modifica o estado atual do programa em execução. [3] Consequentemente, a atribuição depende do conceito de variáveis . Em uma tarefa:

  • O expressioné avaliado no estado atual do programa.
  • É variableatribuído o valor calculado, substituindo o valor anterior dessa variável.

Exemplo: Supondo que ase trate de uma variável numérica, a atribuição a := 2*asignifica que o conteúdo da variável aé duplicado após a execução da instrução.

Um exemplo de segmento de código C :

int x = 10 ; flutuar você ; x = 23 ; y = 32,4f ;    
 
  
  

Neste exemplo, a variável xé declarada primeiro como um int e então recebe o valor 10. Observe que a declaração e a atribuição ocorrem na mesma instrução. Na segunda linha, yé declarado sem atribuição. Na terceira linha, xé reatribuído o valor 23. Por fim, yé atribuído o valor 32,4.

Para uma operação de atribuição, é necessário que o valor de expressionesteja bem definido (é um rvalue válido ) e que variablerepresente uma entidade modificável (é um lvalue modificável (não const ) válido ). Em algumas linguagens, normalmente dinâmicas , não é necessário declarar uma variável antes de atribuir-lhe um valor. Nessas linguagens, uma variável é declarada automaticamente na primeira vez que é atribuída, variando o escopo em que ela é declarada de acordo com a linguagem.

Tarefa única

Qualquer atribuição que altere um valor existente (por exemplo, x := x + 1) não é permitida em linguagens puramente funcionais . [4] Na programação funcional , a atribuição é desencorajada em favor da atribuição única, mais comumente conhecida como inicialização . A atribuição única é um exemplo de vinculação de nome e difere da atribuição descrita neste artigo porque só pode ser feita uma vez, geralmente quando a variável é criada; nenhuma reatribuição subsequente é permitida.

Uma avaliação de uma expressão não tem efeito colateral se não alterar um estado observável da máquina, [5] além de produzir o resultado, e sempre produzir o mesmo valor para a mesma entrada. [4] A atribuição imperativa pode introduzir efeitos colaterais ao destruir e tornar o valor antigo indisponível ao substituí-lo por um novo, [6] e é referida como atribuição destrutiva por esse motivo no LISP e na programação funcional , semelhante à atualização destrutiva .

A atribuição única é a única forma de atribuição disponível em linguagens puramente funcionais, como Haskell , que não possuem variáveis ​​no sentido de linguagens de programação imperativas [4], mas sim valores constantes nomeados possivelmente de natureza composta, com seus elementos definidos progressivamente em- demanda , para as línguas preguiçosas . Linguagens puramente funcionais podem fornecer uma oportunidade para a computação ser realizada em paralelo , evitando o gargalo de von Neumann da execução sequencial passo a passo, uma vez que os valores são independentes uns dos outros. [7]

Linguagens funcionais impuras fornecem atribuição única e também atribuição verdadeira (embora a atribuição verdadeira seja normalmente usada com menos frequência do que em linguagens de programação imperativas). Por exemplo, no Scheme, tanto a atribuição única (com let) quanto a atribuição verdadeira (com set!) podem ser usadas em todas as variáveis, e primitivas especializadas são fornecidas para atualização destrutiva dentro de listas, vetores, strings, etc. No OCaml, apenas atribuição única é permitida para variáveis, através da sintaxe; entretanto, a atualização destrutiva pode ser usada em elementos de arrays e strings com operador separado, bem como em campos de registros e objetos que foram explicitamente declarados mutáveis ​​(ou seja, capazes de serem alterados após sua declaração inicial) pelo programador. let name = value<-

Linguagens de programação funcionais que usam atribuição única incluem Clojure (para estruturas de dados, não vars), Erlang (aceita atribuição múltipla se os valores forem iguais, em contraste com Haskell), F# , Haskell , JavaScript (para constantes), Lava , OCaml , Oz (para variáveis ​​de fluxo de dados, não células), Racket (para algumas estruturas de dados como listas, não símbolos), SASL , Scala (para vals), SISAL , Standard ML . O código Prolog sem retrocesso pode ser considerado de atribuição única explícita , explícito no sentido de que suas variáveis ​​​​(nomeadas) podem estar em um estado explicitamente não atribuído ou ser definidas exatamente uma vez. Em Haskell, por outro lado, não pode haver variáveis ​​não atribuídas, e toda variável pode ser pensada como sendo implicitamente definida, quando é criada, para seu valor (ou melhor, para um objeto computacional que produzirá seu valor sob demanda ).

Valor de uma tarefa

Em algumas linguagens de programação, uma instrução de atribuição retorna um valor, enquanto em outras isso não acontece.

Na maioria das linguagens de programação orientadas a expressões (por exemplo, C ), a instrução de atribuição retorna o valor atribuído, permitindo expressões idiomáticas como x = y = a, nas quais a instrução de atribuição y = aretorna o valor de a, que é então atribuído a x. Em uma instrução como , o valor de retorno de uma função é usado para controlar um loop enquanto atribui esse mesmo valor a uma variável. while ((ch = getchar()) != EOF) {}

Em outras linguagens de programação, Scheme por exemplo, o valor de retorno de uma atribuição é indefinido e tais expressões são inválidas.

Em Haskell , [8] não há atribuição de variáveis; mas operações semelhantes à atribuição (como atribuir a um campo de uma matriz ou a um campo de uma estrutura de dados mutável) geralmente são avaliadas como o tipo de unidade , que é representado como (). Este tipo possui apenas um valor possível, portanto não contém nenhuma informação. Normalmente é o tipo de expressão avaliada apenas por seus efeitos colaterais.

Formas variantes de atribuição

Certos padrões de uso são muito comuns e, portanto, geralmente possuem uma sintaxe especial para apoiá-los. Eles são principalmente açúcar sintático para reduzir a redundância no código-fonte, mas também auxiliam os leitores do código a compreender a intenção do programador e fornecem ao compilador uma pista para uma possível otimização.

Atribuição aumentada

O caso em que o valor atribuído depende de um valor anterior é tão comum que muitas linguagens imperativas, principalmente C e a maioria de seus descendentes, fornecem operadores especiais chamados atribuição aumentada , como *=, então a = 2*apodem ser escritos como a *= 2. [3] Além do açúcar sintático, isso auxilia a tarefa do compilador, deixando claro que a modificação da variável no local aé possível.

Atribuição encadeada

Uma instrução como w = x = y = zé chamada de atribuição encadeada na qual o valor de zé atribuído a múltiplas variáveis w, x,​​e y. Atribuições encadeadas são frequentemente usadas para inicializar múltiplas variáveis, como em

a = b = c = d = f = 0

Nem todas as linguagens de programação suportam atribuição encadeada. As tarefas encadeadas são equivalentes a uma sequência de tarefas, mas a estratégia de avaliação difere entre os idiomas. Para tarefas simples encadeadas, como inicializar múltiplas variáveis, a estratégia de avaliação não importa, mas se os alvos (valores l) na tarefa estiverem conectados de alguma forma, a estratégia de avaliação afetará o resultado.

Em algumas linguagens de programação ( C por exemplo), atribuições encadeadas são suportadas porque as atribuições são expressões e possuem valores. Neste caso, a atribuição em cadeia pode ser implementada com uma atribuição associativa à direita , e as atribuições acontecem da direita para a esquerda. Por exemplo, i = arr[i] = f()é equivalente a arr[i] = f(); i = arr[i]. Em C++ eles também estão disponíveis para valores de tipos de classe, declarando o tipo de retorno apropriado para o operador de atribuição.

Em Python , as instruções de atribuição não são expressões e, portanto, não possuem valor. Em vez disso, as atribuições encadeadas são uma série de instruções com vários destinos para uma única expressão. As atribuições são executadas da esquerda para a direita para que i = arr[i] = f()avalie a expressão f(), em seguida, atribua o resultado ao destino mais à esquerda, ie, em seguida, atribua o mesmo resultado ao próximo destino, arr[i]usando o novo valor de i. [9] Isto é essencialmente equivalente a tmp = f(); i = tmp; arr[i] = tmpque nenhuma variável real seja produzida para o valor temporário.

Atribuição paralela

Algumas linguagens de programação, como APL , Common Lisp , [10] Go , [11] JavaScript (desde 1.7), PHP , Maple , Lua , occam 2 , [12] Perl , [13] Python , [14] REBOL , Ruby , [15] e o PowerShell permitem que diversas variáveis ​​sejam atribuídas em paralelo, com sintaxe como:

uma, b:= 0, 1

que atribui simultaneamente 0 ae 1 a b. Isto é mais conhecido como atribuição paralela ; foi introduzido na CPL em 1963, sob o nome de atribuição simultânea , [16] e às vezes é chamado de atribuição múltipla , embora seja confuso quando usado com "atribuição única", pois não são opostos. Se o lado direito da atribuição for uma única variável (por exemplo, um array ou estrutura), o recurso é chamado de descompactação [17] ou desestruturação da atribuição : [18]

lista de variáveis := {0, 1}
a, b := lista

A lista será descompactada para que 0 seja atribuído ae 1 para b. Além disso,

uma, b := b, uma

troca os valores de ae b. Em linguagens sem atribuição paralela, isso teria que ser escrito para usar uma variável temporária

vart := a
uma:=b
b := t

já que a := b; b := adeixa ambos ae bcom o valor original de b.

Algumas linguagens, como Go , F# e Python , combinam atribuição paralela, tuplas e descompactação automática de tuplas para permitir vários valores de retorno de uma única função, como neste exemplo do Python,

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

enquanto outras linguagens, como C# e Rust , mostradas aqui, exigem construção e desconstrução explícita de tuplas com parênteses:

// Sintaxe válida em C# ou Rust 
( a , b ) = ( b , a );    
// retorno da tupla C# 
( string , int ) f () => ( "foo" , 1 ); var ( a , b ) = f ();     
    
// Retorno da tupla de ferrugem 
let f = || ( "foo" , 1 ); deixe ( uma , b ) = f ();     
    

Isto fornece uma alternativa ao uso de parâmetros de saída para retornar vários valores de uma função. Isso remonta ao CLU (1974), e o CLU ajudou a popularizar a atribuição paralela em geral.

C# permite adicionalmente atribuição de desconstrução generalizada com implementação definida pela expressão no lado direito, à medida que o compilador procura uma instância apropriada ou método de extensão Deconstruct na expressão, que deve ter parâmetros de saída para as variáveis ​​às quais está sendo atribuída. [19] Por exemplo, um método que daria à classe que aparece no mesmo comportamento que o valor de retorno f()acima seria

void Deconstruct ( out string a , out int b ) { a = "foo" ; b = 1 ; }              

Em C e C++, o operador vírgula é semelhante à atribuição paralela, permitindo que múltiplas atribuições ocorram dentro de uma única instrução, escrevendo a = 1, b = 2em vez de a, b = 1, 2. Isso é usado principalmente em loops for e é substituído por atribuição paralela em outras linguagens, como Go. [20] No entanto, o código C++ acima não garante simultaneidade perfeita, uma vez que o lado direito do código seguinte a = b, b = a+1é avaliado após o lado esquerdo. Em linguagens como Python, a, b = b, a+1atribuirá as duas variáveis ​​simultaneamente, usando o valor inicial de a para calcular o novo b.

Atribuição versus igualdade

O uso do sinal de igual =como operador de atribuição tem sido frequentemente criticado, devido ao conflito com igual como comparação de igualdade. Isso resulta em confusão para novatos na escrita de código e confusão até mesmo para programadores experientes na leitura de código. O uso de iguais para atribuição remonta à linguagem Superplan de Heinz Rutishauser , projetada de 1949 a 1951, e foi particularmente popularizada por Fortran:

Um exemplo notório de má ideia foi a escolha do sinal de igual para denotar atribuição. Ela remonta ao Fortran em 1957 [a] e foi copiada cegamente por exércitos de designers de linguagem. Por que é uma má ideia? Porque derruba uma tradição centenária deixar “=” denotar uma comparação para igualdade, um predicado que é verdadeiro ou falso. Mas Fortran fez com que isso significasse atribuição, a imposição da igualdade. Neste caso, os operandos estão em situação desigual: o operando esquerdo (uma variável) deve ser igualado ao operando direito (uma expressão). x = y não significa a mesma coisa que y = x. [21]

-  Niklaus Wirth , Boas ideias, através do espelho

Os programadores iniciantes às vezes confundem atribuição com o operador relacional para igualdade, já que "=" significa igualdade em matemática e é usado para atribuição em muitas linguagens. Mas a atribuição altera o valor de uma variável, enquanto o teste de igualdade testa se duas expressões têm o mesmo valor.

Em algumas linguagens, como BASIC , um único sinal de igual ( "=") é usado tanto para o operador de atribuição quanto para o operador relacional de igualdade, com o contexto determinando o que se entende. Outras línguas usam símbolos diferentes para os dois operadores. Por exemplo:

  • Em ALGOL e Pascal , o operador de atribuição é dois pontos e um sinal de igual ( ":="), enquanto o operador de igualdade é um único igual ( "=").
  • Em C , o operador de atribuição é um único sinal de igual ( "="), enquanto o operador de igualdade é um par de sinais de igual ( "==").
  • Em R , o operador de atribuição é basicamente <-, como em x <- value, mas um único sinal de igual pode ser usado em determinados contextos.

A semelhança nos dois símbolos pode levar a erros se o programador esquecer qual formato (" =", " ==", " :=") é apropriado ou digitar incorretamente " =" quando " ==" era pretendido. Este é um problema de programação comum em linguagens como C (incluindo uma famosa tentativa de backdoor no kernel Linux), [22] onde o operador de atribuição também retorna o valor atribuído (da mesma forma que uma função retorna um valor), e pode ser validamente aninhados dentro de expressões. Se a intenção era comparar dois valores em uma ifinstrução, por exemplo, é muito provável que uma atribuição retorne um valor interpretável como booleano verdadeiro, caso em que a thencláusula será executada, fazendo com que o programa se comporte de forma inesperada. Alguns processadores de linguagem (como gcc ) podem detectar tais situações e avisar o programador sobre o erro potencial.

Notação

As duas representações mais comuns para a atribuição de cópia são sinal de igual ( =) e dois pontos igual ( :=). Ambas as formas podem denotar semanticamente uma instrução de atribuição ou um operador de atribuição (que também possui um valor), dependendo do idioma e/ou uso.

variable = expression Fortran , PL/I , C (e descendentes como C++ , Java , etc.), Bourne shell , Python , Go (atribuição a variáveis ​​pré-declaradas), R , PowerShell , Nim , etc.
variable := expression ALGOL (e derivados), Simula , CPL , BCPL , Pascal [23] (e descendentes como Modula ), Mary , PL/M , Ada , Smalltalk , Eiffel , [24] [25] Oberon , Dylan , [26] Seed7 , Python (uma expressão de atribuição), [27] Go (abreviação para declarar e definir uma variável), [28] Io , AMPL , ML (atribuir a um valor de referência), [29] AutoHotkey etc.

Outras possibilidades incluem uma seta para a esquerda ou uma palavra-chave, embora existam outras variantes mais raras:

variable << expression Magia
variable <- expression F# , OCaml , R , S
variable <<- expression R
assign("variable", expression) R
variableexpression APL , [30] Smalltalk , Programação BÁSICA
variable =: expression J.
LET variable = expression BÁSICO
let variable := expression XQuery
set variable to expression AppleScript
set variable = expression Concha C
Set-Variable variable (expression) PowerShell
variable : expression Macsyma, Maxima , K
variable: expression Rebol
var variable expression Linguagem de script mIRC
reference-variable :- reference-expression Simulação

As atribuições de pseudocódigo matemático são geralmente representadas com uma seta para a esquerda.

Algumas plataformas colocam a expressão à esquerda e a variável à direita:

MOVE expression TO variable COBOL
expressionvariable TI-BASIC , Casio BASIC
expression -> variable POP-2 , BETA , R
put expression into variable Código ao vivo
PUT expression IN variable abc

Algumas linguagens orientadas a expressões, como Lisp [31] [32] e Tcl, usam uniformemente sintaxe de prefixo (ou pós-fixo) para todas as instruções, incluindo atribuição.

(setf variable expression) Lisp comum
(set! variable expression) Esquema [33] [34] [35]
set variable expression Tcl
expression variable ! Adiante

Veja também

Notas

  1. ^ O uso é =anterior ao Fortran, embora tenha sido popularizado pelo Fortran.

Referências

  1. ^ ab "2cs24 Declarativo" . www.csc.liv.ac.uk. ​Arquivado do original em 24 de abril de 2006 . Recuperado em 20 de abril de 2018 .
  2. ^ abc "Programação Imperativa" . uah.edu . Arquivado do original em 4 de março de 2016 . Recuperado em 20 de abril de 2018 .
  3. ^ Ruediger-Marcus Flaig (2008). Programação de bioinformática em Python: um curso prático para iniciantes. Wiley-VCH. pp. 98–99. ISBN 978-3-527-32094-3. Recuperado em 25 de dezembro de 2010 .
  4. ^ abcde Cruzando fronteiras: Explore a programação funcional com Haskell Arquivado em 19 de novembro de 2010, na Wayback Machine , por Bruce Tate
  5. ^ Mitchell, John C. (2003). Conceitos em linguagens de programação. Cambridge University Press. pág. 23.ISBN 978-0-521-78098-8. Recuperado em 3 de janeiro de 2011 .
  6. ^ "Linguagens de programação imperativas (IPL)" (PDF) . gwu.edu . Recuperado em 20 de abril de 2018 .
  7. ^ John C. Mitchell (2003). Conceitos em linguagens de programação. Cambridge University Press. pp. 81–82. ISBN 978-0-521-78098-8. Recuperado em 3 de janeiro de 2011 .
  8. ^ Hudak, Paulo (2000). A Escola de Expressão Haskell: Aprendendo Programação Funcional por Meio de Multimídia . Cambridge: Cambridge University Press. ISBN 0-521-64408-9.
  9. ^ "7. Instruções simples - documentação do Python 3.6.5" . docs.python.org . Recuperado em 20 de abril de 2018 .
  10. ^ "CLHS: Macro SETF, PSETF" . Hiperespecificação Lisp comum . LispWorks . Recuperado em 23 de abril de 2019 .
  11. ^ A especificação da linguagem de programação Go: atribuições
  12. ^ INMOS limitado, ed. (1988). Manual de Referência Occam 2 . Nova Jersey: Prentice Hall. ISBN 0-13-629312-3.
  13. ^ Parede, Larry ; Christiansen, Tom; Schwartz, Randal C. (1996). Linguagem de programação Perl (2 ed.). Cambridge: O'Reilly. ISBN 1-56592-149-6.
  14. ^ Lutz, marca (2001). Linguagem de programação Python (2 ed.). Sebastopol: O'Reilly. ISBN 0-596-00085-5.
  15. ^ Tomás, David; Caça, Andrew (2001). Programando Ruby: O Guia Pragmático do Programador. Alto Rio Saddle: Addison Wesley. ISBN 0-201-71089-7.
  16. ^ DW Barron e outros. , "As principais características do CPL", Computer Journal 6 :2:140 (1963). texto completo (assinatura)
  17. ^ "PEP 3132 - Desembalagem Iterável Estendida" . legado.python.org . Recuperado em 20 de abril de 2018 .
  18. ^ “Tarefa de desestruturação” . Documentos da Web do MDN . Recuperado em 20 de abril de 2018 .
  19. ^ “Desconstruindo tuplas e outros tipos” . Documentos Microsoft . Microsoft . Recuperado em 29 de agosto de 2019 .
  20. ^ Efetivo Go: for, "Finalmente, Go não tem operador vírgula e ++ e -- são instruções, não expressões. Portanto, se você deseja executar múltiplas variáveis ​​em um for, você deve usar atribuição paralela (embora isso impeça ++ e -- )."
  21. ^ Niklaus Wirth. “Boas ideias, através do espelho” . CiteSeerX 10.1.1.88.8309 . 
  22. ^ Corbet (6 de novembro de 2003). "Uma tentativa de backdoor do kernel" .
  23. ^ Moore, Lawrie (1980). Fundamentos de Programação com Pascal . Nova York: John Wiley & Sons. ISBN 0-470-26939-1.
  24. ^ Meyer, Bertrand (1992). Eiffel, a Língua . Hemel Hempstead: Prentice Hall International (Reino Unido). ISBN 0-13-247925-7.
  25. ^ Wiener, Richard (1996). Uma introdução orientada a objetos à ciência da computação usando Eiffel . Upper Saddle River, Nova Jersey: Prentice Hall. ISBN 0-13-183872-5.
  26. ^ Feinberg, Neal; Keene, Sonya E.; Mathews, Robert O.; Withington, P. Tucker (1997). Programação Dylan . Massachusetts: Addison Wesley. ISBN 0-201-47976-1.
  27. ^ “PEP 572 – Expressões de Atribuição” . python.org . 28 de fevereiro de 2018 . Recuperado em 4 de março de 2020 .
  28. ^ "Especificação da linguagem de programação Go - A linguagem de programação Go" . golang.org . Recuperado em 20 de abril de 2018 .
  29. ^ Ullman, Jeffrey D. (1998). Elementos de programação de ML: edição ML97 . Englewood Cliffs, Nova Jersey: Prentice Hall. ISBN 0-13-790387-1.
  30. ^ Iverson, Kenneth E. (1962). Uma linguagem de programação. John Wiley e Filhos. ISBN 0-471-43014-5. Arquivado do original em 04/06/2009 . Recuperado em 09/05/2010 .
  31. ^ Graham, Paulo (1996). Lisp comum ANSI. Nova Jersey: Prentice Hall. ISBN 0-13-370875-6.
  32. ^ Steele, cara L. (1990). Lisp comum: a linguagem . Lexington: Imprensa Digital. ISBN 1-55558-041-6.
  33. ^ Dybvig, R. Kent (1996). A linguagem de programação do esquema: esquema ANSI . Nova Jersey: Prentice Hall. ISBN 0-13-454646-6.
  34. ^ Smith, Jerry D. (1988). Introdução ao Esquema . Nova Jersey: Prentice Hall. ISBN 0-13-496712-7.
  35. ^ Abelson, Haroldo; Sussman, Gerald Jay; Sussman, Julie (1996). Estrutura e Interpretação de Programas de Computador . Nova Jersey: McGraw-Hill. ISBN 0-07-000484-6.
Obtido em "https://en.wikipedia.org/w/index.php?title=Assignment_(computer_science)&oldid=1169859346"