Por que o operador de atribuição atribui ao lado esquerdo?

47

Comecei a ensinar a um amigo programação recentemente (estamos usando Python) e, quando começamos a discutir a criação de variáveis ​​e o operador de atribuição, ela perguntou por que o valor à direita é atribuído ao nome à esquerda e não vice-versa .

Eu não tinha pensado muito nisso antes, porque parecia natural para mim, mas ela disse que da esquerda para a direita parecia mais natural para ela, já que é assim que a maioria de nós lê línguas naturais.

Pensei sobre isso e concluí que torna o código muito mais fácil de ler, pois os nomes atribuídos (aos quais o programador precisará reutilizar) são facilmente visíveis, alinhados à esquerda.

aligned = 2
on = 'foo' + 'bar' + 'foobar'
the = 5.0 / 2
left = 2 + 5

Ao contrário de:

2 = aligned 
'foo' + 'bar' + 'foobar' = on
5.0 / 2 = the 
2 + 5 = right 

# What were the names again...?

Agora, eu me pergunto se há outras razões também para esse padrão. Existe uma história por trás disso? Ou há alguma razão técnica para que essa seja uma boa opção (não sei muito sobre compiladores)? E há alguma linguagem de programação atribuída ao lado direito?

voithos
fonte
15
R pode atribuir ao lado direito ( value -> variable).
Você
Talvez isso se deva a nomes de variáveis ​​incorretos? Que tal 'numAligned é 2' e 'chickensLeft é 2 + 5'?
Job
18
O nome do seu amigo é ... Yoda?
Adrian
1
Se ela começar a reclamar sobre isso, eu me pergunto o que ela fará quando vir C ++ & co.
BlackBear
3
Apenas uma observação: a Khan Academy tem algumas lições sobre Python para programadores iniciantes: khanacademy.org/#computer-science e o Google tem algumas sobre mais avançadas: code.google.com/edu/languages/google-python-class/set- up.html
lindon fox

Respostas:

56

O mesmo vale para @paxdiablo. As primeiras linguagens de programação foram escritas por matemáticos - na verdade todas elas foram. Em matemática, por seu próprio princípio - ler da esquerda para a direita - faz sentido na maneira como funciona.

x = 2y - 4.

Em matemática, você diria o seguinte: Seja x igual a 2y -4.

Além disso, mesmo em álgebra, você faz isso. Ao resolver uma equação para uma variável, você isola a variável que está resolvendo no lado esquerdo. ie y = mx + b;

Além disso, quando uma família inteira de idiomas - como a família C - tem uma certa sintaxe, é mais caro mudar.

Jonathan Henson
fonte
19
@ Farmacoy: em matemática, atribuição e igualdade são a mesma coisa, pois não existe uma seqüência em uma fórmula como existe em computadores. (A é igual a b e, ao mesmo tempo b é igual a)
Petruza
1
@FB, exceto em algumas linguagens funcionais de atribuição única, como, por exemplo, Erlang. Atribuição e assegurando a igualdade são os mesmos, como na matemática
Par Stritzinger
18
@Petruza Não, na atribuição de matemática e na igualdade não são a mesma coisa. Se eu disser 'Let x = 2y - 3', é diferente de 'Assim x = 2y - 3'. Em matemática, o contexto tipicamente os diferencia. Como o comentário que me contestou foi tão aclamado universalmente, mencionarei que tenho um Ph.D. em matemática, tenho certeza disso.
Eric Wilson
2
Eu não sei matemática em nenhum lugar perto de um doutorado, o que afirmo é que, como não há seqüencialidade, não há ordem de execução em matemática, tanto em uma tarefa como em uma igualdade, ao contrário da programação, na qual os dois lados de uma tarefa podem ser diferentes em algum momento e acabam sendo iguais em outro momento. Mas, em matemática, em uma tarefa como se let a be...não houvesse tempo, os dois lados da tarefa também são iguais; portanto, a tarefa é de fato uma igualdade, não é à toa que os dois usam o mesmo sinal:=
Petruza 04/04/11
5
@Petruzza - mas não é sequentiality. Os documentos matemáticos são escritos do início ao fim, da mesma forma que em qualquer outro documento. Se afirmo x = 1no capítulo um, mas afirmo x = 2no capítulo dois, isso não é uma terrível contradição - cada afirmação se aplica apenas dentro de um determinado contexto. A diferença na programação imperativa é em parte a remoção de uma barreira (não precisamos de uma mudança de contexto) e em parte a implementação e a utilidade.
precisa saber é o seguinte
26

BASIC, uma das linguagens de computador mais antigas tinha a forma "adequada" de:

10 LET AREA = HEIGHT * WIDTH

que corresponde à mentalidade matemática de especificar uma variável, como "Seja H a altura do objeto".

COBOLtambém foi semelhante com sua COMPUTEafirmação. Como em muitas maneiras de fazer as coisas, pode ter sido simplesmente uma decisão arbitrária que foi levada adiante por vários idiomas.


fonte
7
Suponho que esse formulário seja mais natural quando as linhas do programa são consideradas "instruções" em oposição a "operações". Como em I declare that X must equal THIS, em vez do mais linearEvaluate THIS and store it in X
voithos
Espere, o BASIC era uma linguagem de computador 'precoce'?
Alex Feinman
2
@ Alex Considerando que isso remete aos anos 1960, eu diria que é bem cedo.
Ben Richards
1
Por outro lado, em COBOL você pode escrever MULTIPLY HEIGHT BY WIDTH GIVING AREA, portanto, a variável que obtém o resultado fica no lado direito da instrução.
User281377
25

Na verdade, existe uma linguagem de programação que atribui ao lado direito: TI-BASIC ! Não apenas isso, mas também não usa '=' como operador de atribuição, mas usa uma seta conhecida como operador "STO".

exemplos:

5→A
(A + 3)→B
(A - B)→C

No exemplo acima, três variáveis ​​estão sendo declaradas e recebem valores. A seria 5, B seria 8 e C seria -3. A primeira declaração / atribuição pode ser lida 'loja 5 como A'.

Quanto ao motivo pelo qual o TI-BASIC usa esse sistema para atribuição, atribuo-o a ser porque é uma linguagem de programação para uma calculadora. O operador "STO" nas calculadoras de TI era usado com mais frequência nas operações normais da calculadora depois que um número era calculado. Se fosse um número que o usuário desejasse lembrar, eles pressionariam o botão "STO" e o caclulator solicitaria um nome (ativando automaticamente o bloqueio alfa para que as teclas digitassem letras em vez de números):

Sin(7 + Cos(3))
                    -.26979276
Ans→{variable name}
                    -.26979276

e o usuário poderia nomear a variável como quisesse. Tendo que ativar a trava alfa, digite o nome e pressione "STO", e pressionar a tecla "Ans" teria sido muito complicado para operações normais. Como todas as funções da calculadora estão disponíveis no TI-BASIC, nenhum outro operador de atribuição foi adicionado, pois o "STO" executou a mesma tarefa, embora ao contrário, quando comparado à maioria dos outros idiomas.

(Anedota: TI-BASIC foi uma das primeiras linguagens que aprendi, então, quando aprendi Java na faculdade, senti como se atribuir à ESQUERDA fosse incomum e 'invertido'!)

diceguyd30
fonte
+1 Eu esqueci totalmente isso! O TI Basic também foi meu primeiro idioma, mas não me lembro desse detalhe.
Barjak
Na verdade, o operador STO está mais próximo de como a máquina funciona e de como qualquer idioma realmente opera. O valor é calculado primeiro e depois armazenado na memória.
Kratz
15

Heurística 1: Quando você se deparar com mais de uma maneira possível de fazer algo enquanto cria uma linguagem, escolha a mais comum e mais intuitiva, ou então você terminará com o Perl +.

Agora, como é mais natural (pelo menos para um falante de inglês)? Vejamos como escrevemos / dizemos coisas em inglês:

Steven agora tem 10 anos (em oposição a 10 anos, Steven agora tem). Eu peso mais de 190 libras (em oposição a mais de 190 libras eu peso).

Em código:

steven = 10
i > 190

O seguinte também parece mais natural:

"Se Mary tem 18 anos, ela pode comer um doce". "Se eu tenho menos de 21 anos, pedirei a meu irmão por tequila".

if (mary == 18) { ... }
if (i < 21) { ... }

do que:

"Se 18 anos Maria é ..." "Se 21 é maior que minha idade ..."

Agora o código:

if (18 == mary) { ... }
if (21 > i) { ... }

Observe que isso não é natural para programadores nem falantes de inglês. As frases soam como yoda-speak, e o código é apelidado de yoda-conditions. Isso pode ser útil em C ++, mas tenho certeza de que a maioria das pessoas concorda: se um compilador pudesse fazer o trabalho pesado e aliviar a necessidade de condições de yoda, a vida seria um pouco mais fácil.

Claro, alguém poderia se acostumar com qualquer coisa. Por exemplo, o número 81 é escrito como:

Oitenta e um (inglês) Oitenta e um (espanhol) Um e oitenta (alemão).

Finalmente, existem 4! = 24 maneiras válidas de dizer "maçã verde está na mesa" em russo - a ordem (quase) não importa, exceto que 'on' deve vir junto com 'table'. Portanto, se você é um falante nativo de russo (por exemplo), pode não se importar se alguém escreve a = 10ou 10 = aporque ambos parecem igualmente naturais.

Embora a lingüística seja um assunto fascinante, nunca a estudei formalmente e não conheço muitas línguas. Espero ter fornecido contra-exemplos suficientes.

Trabalho
fonte
4
... e em francês, 81 é dito como "quatro vezes vinte e um" ... :)
Martin Sojka
1
@ Martin, acredito dinamarquês é muito semelhante.
um CVn
7
@ Martin Isso é realmente estranho, porque quatro vezes vinte e um é 84.
Peter Olson
6
@ Pedro: você tem a sua errado parêntesis, é(four times twenty) one
SingleNegationElimination
4
@Job: Lendo o seu "Se Mary tem 18 anos ...", eu fui inevitavelmente lembrado de Yoda dizendo "Quando você chega aos novecentos anos, parece tão bom que não vai, hum?" em Return of the Jedi :-)
joriki
11

Tudo começou com a FORTRAN nos anos 50. Onde FORTRAN era uma abreviação de FORmula TRANslation - as fórmulas em questão eram simples equações algébricas que por convenção sempre atribuem à esquerda.

Seu COBOL quase contemporâneo, por outro lado, deveria ser semelhante ao inglês e atribuído à direita (principalmente!).

MOVE 1 TO COUNTER.
ADD +1 TO LINE-CNT.
MULTIPLY QTY BY PRICE GIVING ITEM-PRICE.
James Anderson
fonte
Acho que este é o melhor exemplo aqui, porque ilustra perfeitamente um contexto em um idioma em que é legível para um falante de inglês comum, mas tem a atribuição correta. Estou dizendo que isso é importante por causa da grande quantidade de pessoas dizendo em inglês que apenas leria da esquerda para a direita para que a tarefa fizesse sentido, o que não é absolutamente verdade.
Joshua Hedges
5

Bem, como @ diceguyd30 apontou, existem duas notações.

  • <Identifier> = <Value>significa "permitir que o identificador seja valor ". Ou para expandir: Defina (ou redefina) a variável Identifier para Value .
  • <Value> -> <Identifier>significa "armazenar valor para o identificador ". Ou para expandir: Coloque Valor no local designado pelo Identificador .

Obviamente, de um modo geral, o Identificador pode ser de fato qualquer valor de L.

A primeira abordagem respeita o conceito abstrato de variáveis, a segunda abordagem é mais sobre armazenamento real.

Observe que a primeira abordagem também é comum em idiomas que não possuem atribuições. Além disso, observe que a definição variável e atribuição estão relativamente perto <Type> <Identifier> = <Value>vs. <Identifier> = <Value>.

back2dos
fonte
3

Como já foi mencionado, muito bem todas as primeiras linguagens de computador funcionavam dessa maneira. Por exemplo, FORTRAN, que surgiu muitos anos antes do BASIC.

Na verdade, faz muito sentido ter a variável atribuída à esquerda da expressão de atribuição. Em alguns idiomas, você pode ter várias rotinas sobrecarregadas diferentes com o SAME NAME, retornando diferentes tipos de resultado. Ao permitir que o compilador veja primeiro o tipo da variável atribuída, ele sabe qual rotina sobrecarregada chamar ou que conversão implícita gerar ao converter de (por exemplo) um número inteiro para um float. Essa é uma explicação simplista, mas espero que você entenda.

Dave Jewell
fonte
2
Entendo sua explicação, mas o compilador não poderia esperar até o final da declaração?
voithos 3/08
Isso pode tornar o lexer mais simples nas linguagens de hoje, mas quantas linguagens de programação até suportam métodos nomeados, e muito menos sobrecargas de métodos, quando esse tipo de sintaxe era novo?
um CVn
2
Olá @voithos. Sim - o compilador poderia olhar para o futuro, mas isso provavelmente teria sido um nível inaceitável de complexidade nos primeiros dias da escrita do compilador - que geralmente era um montador codificado à mão! Penso que colocar a variável atribuída à esquerda é uma escolha pragmática: é mais fácil para o homem e a máquina analisar.
Dave Jewell
Eu acho que seria trivial para uma tarefa atribuir à direita. Quando uma expressão como 3 + 4 == 6 + 7, ambos os lados são avaliados antes do operador, porque o idioma é definido recursivamente. O elemento da linguagem 'variável = expressão' pode ser facilmente alterado para 'expressão = variável'. Se isso causa ou não situações ambíguas depende do restante do idioma.
Kratz
@ Kratz - isso certamente é verdade para compiladores agora, mas pode ter havido um problema menor em linguagens interpretadas muito antigas que funcionavam com fontes tokenizadas. OTOH, isso pode ter favorecido a variável à direita, em vez da variável à esquerda.
Steve314
3

Pode ser um remanescente de algoritmos de análise antecipada. Lembre-se de que a análise de LR foi inventada apenas em 1965, e pode ser que os analisadores de LL tenham problemas (dentro das limitações de tempo e espaço das máquinas da época) ao contrário. Considerar:

identifier = function();
function();

Os dois estão claramente desambiguados do segundo token. Por outro lado,

function() = identifier;
function();

Não tem graça. Isso piora quando você começa a aninhar expressões de atribuição.

function(prev_identifier = expression) = identifier;
function(prev_identifier = expression);

Obviamente, mais fácil desambiguar para máquinas também significa mais desambiguar para humanos. Outro exemplo fácil seria procurar a inicialização de qualquer identificador.

identifier1 = expressionOfAnArbitraryLength;
identifier2 = expressionOfAReallyReallyReallyArbitraryLength;
identifier3 = expression;
identifier4 = AlongLineExpressionWithAFunctionCallWithAssignment(
    identifier = expr);

Fácil, basta olhar para o lado esquerdo. Lado direito, por outro lado

expressionOfAnArbitraryLength = identifier1;
expressionOfAReallyReallyReallyArbitraryLength = identifier2;
expression = identifier3;
AlongLineExpressionWithAFunctionCallWithAssignment(expr = identifier
    ) = identifier4;

Especialmente quando você não pode grepperfurar cartões, é muito mais difícil encontrar o identificador desejado.

DeadMG
fonte
Precisamente o ponto em que pensei, sim.
voithos 8/08/11
2

Os idiomas de montagem têm o destino como parte do código de operação esquerdo. Os idiomas de nível superior tendem a seguir as convenções dos idiomas anteriores.

Quando vir =(ou :=para dialetos pascales), você poderá pronunciá-los como is assigned the value, então a natureza da esquerda para a direita fará sentido (porque também lemos da esquerda para a direita na maioria dos idiomas). Como as linguagens de programação foram predominantemente desenvolvidas por pessoas que leem da esquerda para a direita, as convenções continuaram.

É um tipo de dependência de caminho . Suponho que se a programação de computadores foi inventada por pessoas que falavam hebraico ou árabe (ou algum outro idioma da direita para a esquerda), então suspeito que estaríamos colocando o destino à direita.

Tangurena
fonte
Sim, mas eu suspeito que o texto nos editores estaria alinhado à direita, bem ...
voithos
8
Você não pode generalizar assim sobre linguagens assembly. Eles variam de onde está o operando de destino.
quickly_now
2
@quickly_now: à direita; de fato, a maioria das linguagens de máquina primitivas (nem mesmo os montadores pelos padrões de hoje) nem sequer tinham destino, pois geralmente havia apenas um ou dois acumuladores de uso geral. a maioria das operações implicava o acumulador como destino, exceto os códigos de operação 'store', que especificavam apenas o endereço da memória e não a fonte (que era o acumulador). Realmente não acho que tenha havido influência na sintaxe de atribuição para idiomas semelhantes ao ALGOL.
Javier
3
@Tangurena - Algumas linguagens assembler têm a destimação à esquerda. Não do opcode (esse é o código do objeto montado), mas à esquerda da lista de argumentos para a instrução mnemônica. No entanto, outros têm o destino à direita. No 68000 assembler, você escreveria mov.b #255, d0, por exemplo, onde d0está o registro ao qual atribuir. Montadores mais antigos têm apenas um único argumento por instrução. No 6502 LDA #255(Acumulador de Carga), você pode argumentar que Aestá à esquerda, mas também à esquerda em STA wherever(Acumulador de Loja).
precisa saber é o seguinte
2
E até o Intel 4004 (o ancestral final de 4 bits da família 8086, com os 8008 e 8080 no meio) foi desenvolvido após a atribuição em idiomas de alto nível. Se você está assumindo que a série 8086 é representativa do que os montadores fizeram nos anos 50 e anteriores, duvido muito que isso seja verdade.
precisa saber é o seguinte
2

Por que vale a pena, a maioria das declarações em COBOL lido da esquerda para a direita, de modo que os dois operandos foram nomeados em primeiro lugar, e o destino último, como: multiply salary by rate giving tax.

No entanto, não vou sugerir que seu aluno prefira COBOL, por medo de que eu seja (com toda a razão) sinalizado por fazer um comentário tão baixo, rude e sem gosto! :-)

Jerry Coffin
fonte
1

ela disse que da esquerda para a direita lhe parecia mais natural, pois é assim que a maioria de nós lê as línguas naturais.

Eu acho que isso é um erro. Por um lado, você pode dizer "atribuir 10 para x" ou "mover 10 para x". Por outro lado, você pode dizer "defina x como 10" ou "x se torna 10".

Em outras palavras, dependendo da sua escolha de verbo, a variável atribuída pode ou não ser o assunto e pode ou não estar à esquerda. Portanto, "o que é natural" depende inteiramente de sua escolha habitual de redação para representar a tarefa.

Steve314
fonte
0

No pseudocódigo, o operador de atribuição é muito comumente escrito à direita. Por exemplo

2*sqrt(x)/(3+y) -> z

Nas calculadoras Casio, mesmo variantes não programáveis, a variável de atribuição também é exibida à direita

A+2B → C

Em quarto lugar a variável também está à direita

expression variable !

No x86, a sintaxe da Intel tem o destino à esquerda, mas a sintaxe do GAS inverte a ordem, causando confusão para muitas pessoas, especialmente nas instruções sobre a ordem dos parâmetros, como subtração ou comparação. Estas instruções são iguais em 2 dialetos diferentes

mov rax, rbx    ; Intel syntax
movq %rbx, %rax ; GAS syntax

Ambos movem o valor em rbx para rax. Nenhuma outra linguagem assembly que conheço escreve o destino à direita como GAS.

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

MOVE expression TO variable      COBOL
expression → variable            TI-BASIC, Casio BASIC
expression -> variable           BETA, R
put expression into variable     LiveCode

https://en.wikipedia.org/wiki/Assignment_%28computer_science%29#Notation

A maioria dos idiomas atribui o valor à esquerda, sendo um dos motivos fáceis de alinhar os operadores, mais fáceis de ler e reconhecer a variável, pois as posições dos operadores e das variáveis ​​de atribuição não variam muito nas linhas, e é mais fácil ler como "deixe a variável ter algum valor".

No entanto, algumas pessoas preferem dizer "mova o valor x para y" e escreva a variável à direita.

phuclv
fonte
-1

Eu acho que segue uma maneira lógica de pensar.
Tem que haver uma caixa (variável) primeiro, então você coloca um objeto (valor) dentro dela.
Você não coloca o objeto no ar e depois coloca uma caixa em torno dele.

Petruza
fonte
2
sim você faz. na maioria dos idiomas, o lado direito é avaliado antes do esquerdo.
Javier
3
É uma coisa do tipo "Dirigir no lado direito da estrada". Parece lógico, mas apenas porque é assim que você sempre fez. Todos os países superiores dirigem à esquerda.
James Anderson
1
@JamesAnderson países superiores? : o
nawfal
Você está certo, é lógico que um sistema de escrita da esquerda para a direita seja o alfabeto romano, que eu acho que é usado por quase todas as linguagens de programação, se não todas.
Petruza