4.1.5 Dados como Programas
Ao pensar sobre um programa JavaScript que avalia declarações e expressões JavaScript, uma analogia pode ser útil. Uma visão operacional do significado de um programa é que um programa é uma descrição de uma máquina abstrata (talvez infinitamente grande). Por exemplo, considere o programa familiar para computar fatoriais:
function factorial(n) {
return n === 1
? 1
: factorial(n - 1) * n;
}
Podemos considerar este programa como a descrição de uma máquina contendo partes que decrementam, multiplicam e testam igualdade, juntamente com um switch de duas posições e outra máquina de fatorial. (A máquina de fatorial é infinita porque contém outra máquina de fatorial dentro dela.) A figura 4.2 é um diagrama de fluxo para a máquina de fatorial, mostrando como as partes são conectadas.
Figura 4.2: O programa fatorial, visto como uma máquina abstrata. A máquina contém um teste de igualdade (vermelho), operações de decremento e multiplicação (azul), e recursivamente contém outra máquina de fatorial (amarelo tracejado).
De maneira similar, podemos considerar o avaliador como uma máquina muito especial que recebe como entrada uma descrição de uma máquina. Dada esta entrada, o avaliador se configura para emular a máquina descrita. Por exemplo, se alimentarmos nosso avaliador com a definição de factorial, como mostrado na figura 4.3, o avaliador será capaz de computar fatoriais.
Figura 4.3: O avaliador emulando uma máquina de fatorial. O avaliador atua como uma máquina universal que recebe a definição da função factorial como entrada, analisa-a sintaticamente, e através do ciclo evaluate-apply, emula internamente a máquina de fatorial descrita na Figura 4.2.
Desta perspectiva, nosso avaliador é visto como uma máquina universal. Ele imita outras máquinas quando estas são descritas como programas JavaScript.1
Isso é impressionante. Tente imaginar um avaliador análogo para circuitos elétricos. Este seria um circuito que recebe como entrada um sinal codificando os planos para algum outro circuito, como um filtro. Dada esta entrada, o avaliador de circuito então se comportaria como um filtro com a mesma descrição. Tal circuito elétrico universal é quase inimaginavelmente complexo. É notável que o avaliador de programa é um programa relativamente simples.2
Outro aspecto impressionante do avaliador é que ele age como uma ponte entre os objetos de dados que são manipulados por nossa linguagem de programação e a própria linguagem de programação. Imagine que o programa avaliador (implementado em JavaScript) está executando, e que um usuário está digitando programas para o avaliador e observando os resultados. Da perspectiva do usuário, um programa de entrada como x * x; é um programa na linguagem de programação, que o avaliador deve executar. Da perspectiva do avaliador, no entanto, o programa é simplesmente uma string ou — após análise (parsing) — uma representação de lista marcada que deve ser manipulada de acordo com um conjunto bem definido de regras.
Que os programas do usuário sejam os dados do avaliador não precisa ser uma fonte de confusão. De fato, às vezes é conveniente ignorar esta distinção, e dar ao usuário a capacidade de avaliar explicitamente uma string como uma declaração JavaScript, usando a função primitiva eval do JavaScript que recebe como argumento uma string. Ela analisa a string e — desde que seja sintaticamente correta — avalia a representação resultante no ambiente no qual eval é aplicado. Assim,
eval("5 * 5;");
e
evaluate(parse("5 * 5;"), the_global_environment);
ambos retornarão 25.3
Dada uma função f de um argumento e um objeto a, f é dito "parar" em a se avaliar a expressão f(a) retornar um valor (ao contrário de terminar com uma mensagem de erro ou executar para sempre). Mostre que é impossível escrever uma função halts que determine corretamente se f para em a para qualquer função f e objeto a.
Use o seguinte raciocínio: Se você tivesse tal função halts, você poderia implementar o seguinte programa:
function run_forever() { return run_forever(); }
function strange(f) {
return halts(f, f)
? run_forever()
: "halted";
}
Agora considere avaliar a expressão strange(strange) e mostre que qualquer resultado possível (ou parar ou executar para sempre) viola o comportamento pretendido de halts.4
📝 Encontrou algo errado nesta página?
Sua ajuda é muito importante para melhorar a qualidade da tradução!
🐛 Encontrou um erro?
Se você encontrou:
- Erro de tradução (palavra incorreta, termo técnico errado)
- Erro de ortografia ou gramática
- Link quebrado
- Código de exemplo que não funciona
- Problema de formatação
❓ Tem uma dúvida?
Se você tem:
- Dúvida sobre o conteúdo desta seção
- Pergunta sobre um conceito do SICP
- Dificuldade em entender algum exemplo
- Questão sobre a tradução de algum termo
💡 Tem uma sugestão de melhoria?
Se você quer sugerir:
- Melhoria na explicação
- Exemplo adicional
- Recurso visual (diagrama, ilustração)
- Qualquer outra ideia
🌍 Quer discutir a tradução?
Se você quer debater:
- Escolha de tradução de algum termo
- Consistência de terminologia
- Nuances do português
Obrigado por ajudar a melhorar o SICP.js PT-BR! ✨