O que você está perguntando é essencialmente a diferença entre o poder computacional e o que é comumente chamado de poder expressivo (ou apenas expressividade ) de uma linguagem (ou sistema de computação).
Poder computacional
O poder computacional refere-se a quais tipos de problemas a linguagem pode computar. A classe mais conhecida de potência computacional é aquela que é equivalente a uma Máquina Universal de Turing . Há uma série de outros sistemas de computação, tais como Random Access Machines , λ-cálculo , SK cálculo combinator , funções u-recursiva , WHILE
programas, e muitos outros. E, como se vê, tudo isso pode simular um ao outro, o que significa que todos eles têm o mesmo poder computacional.
Isso dá origem à tese de Church-Turing (em homenagem a Alonzo Church, que criou o λ-cálculo e Alan Turing, que criou a Universal Turing Machine). A tese de Church-Turing é uma hipótese sobre computabilidade com dois aspectos:
- todos os sistemas de computação capazes de computação geral são igualmente poderosos, e
- um ser humano seguindo um algoritmo pode calcular exatamente as funções que uma Máquina de Turing (e, portanto, qualquer outro sistema) pode calcular.
O segundo é mais importante no campo da filosofia da mente do que na ciência da computação.
No entanto, existem duas coisas que a Tese da Igreja não diz, que são muito relevantes para a sua pergunta:
- quão eficientes são as várias simulações e
- quão conveniente é a codificação de um problema.
Um exemplo simples para (1): em uma máquina de acesso aleatório, copiar uma matriz leva um tempo proporcional ao comprimento da matriz. Em uma Máquina de Turing, no entanto, leva um tempo proporcional ao quadrado do comprimento da matriz, porque a Máquina de Turing não possui acesso aleatório à memória, ela pode se mover pela fita uma célula por vez. Portanto, ele precisa se mover pelos n elementos da matriz n vezes para copiá-los. Portanto, diferentes modelos de computação podem ter características de desempenho diferentes, mesmo no caso assintótico, onde tentamos abstrair dos detalhes da implementação.
Existem muitos exemplos para (2): tanto o cálculo λ quanto o Python são completos em Turing. Mas você prefere escrever um programa em Python ou em λ-calculus?
Há também uma terceira ruga que eu contornei até agora: todos esses sistemas originais foram projetados por lógicos, filósofos ou matemáticos, não por cientistas da computação ... simplesmente porque os computadores e, portanto, a ciência da computação não existiam. Tudo isso remonta ao início dos anos 30, mesmo antes das primeiras experiências de Konrad Zuse (que não eram programáveis e / ou concluídas por Turing). Eles falam apenas de "funções computáveis nos números naturais".
Agora, como se vê, há muito que você pode expressar como funções em números naturais - afinal, nossos computadores modernos ainda se dão bem com muito menos que isso (basicamente 3-4 funções nos números 0 e 1, e é isso ), mas, por exemplo, que função um sistema operacional calcula?
Essa noção de E / S, efeitos colaterais, interagindo com o meio ambiente, não é capturada pela idéia de "funções sobre números naturais". E, no entanto, é meio importante, pois, como Simon Peyton Jones disse uma vez "Toda uma função pura sem efeitos colaterais faz com que sua CPU fique quente" , à qual um membro da platéia respondeu "na verdade, esse é um lado -efeito também! "
Edwin Brady , o designer de Idris , (apenas metade) usa de brincadeira (não sei se ele o inventou) o termo "Tetris-complete" para expressar essa diferença entre "pode calcular qualquer função computável em números naturais" e "pode ser usado para escrever programas não triviais que interagem com o ambiente ". Ainda mais ironicamente, ele demonstra isso implementando um clone do Space Invaders em Idris , mas ele diz estar confiante de que Tetris se reduz a Space Invaders.
Outra coisa a salientar é que não apenas a equivalência de Turing não é necessariamente suficiente para falar sobre realmente escrever programas "úteis", como também a OTOH nem mesmo é necessária . Por exemplo, o SQL só se tornou equivalente a Turing com o ANSI SQL: 1999 , mas ainda era útil antes disso. De fato, alguns podem argumentar que torná-lo equivalente a Turing não aumentou sua utilidade. Existem muitos idiomas específicos do domínio que não são equivalentes ao Turing. O idioma de descrição de dados normalmente não é (e não deveria ser). O Total Languages obviamente não pode ser equivalente a Turing, mas você ainda pode gravar loops de eventos, servidores da Web ou sistemas operacionais neles. Também existem idiomas equivalentes a Turing, mas onde isso é realmente considerado um erro.
Portanto, apesar de tudo, a equivalência de Turing não é muito interessante, a menos que você queira analisar estaticamente os programas.
Expressividade
Supondo que nosso sistema de computação seja computacionalmente poderoso o suficiente para resolver nosso problema, o que temos a fazer a seguir é expressar nosso algoritmo para resolver esse problema em algum tipo de notação formal para esse sistema. Em outras palavras: precisamos escrever um programa em alguma linguagem de computador. É aí que entra a noção de expressividade .
Refere-se, essencialmente, a quão "fácil" ou "agradável" é escrever nosso programa em nossa linguagem de programação específica. Como você pode ver, a noção é bastante vaga, subjetiva e mais psicológica do que técnica.
No entanto, existem tentativas de definições mais precisas. A mais famosa (e a mais rigorosa que conheço) é de Matthias Felleisen em seu artigo Sobre o poder expressivo das linguagens de programação (as duas primeiras páginas contêm uma introdução suave, o restante do artigo é mais carnudo).
A principal intuição é a seguinte: ao traduzir um programa de um idioma para outro idioma, algumas das alterações que você precisa fazer estão localmente contidas (como, por exemplo, transformar FOR
loops em WHILE
loops ou loops em GOTO
s condicionais ), e algumas exigem uma alteração no global estrutura do programa.
Quando você pode substituir um recurso de um idioma por um recurso diferente de um idioma diferente apenas por transformações locais, diz-se que esses recursos não afetam o poder expressivo. Isso é chamado de açúcar sintático .
Por outro lado, se exigir uma alteração na estrutura global do programa, o idioma para o qual você está traduzindo será incapaz de expressar o recurso. E o idioma do qual você está traduzindo é mais expressivo (com relação a esse recurso).
Observe que isso fornece uma definição objetiva mensurável de expressividade. Observe também que a noção depende do contexto do recurso e é comparativa. Portanto, se todo programa no idioma A puder ser traduzido para o idioma B apenas com alterações locais, e houver pelo menos um programa no idioma B que não possa ser traduzido para A com apenas alterações locais, o idioma B será estritamente mais expressivo que o idioma UMA. No entanto, o cenário mais provável é que muitos programas nos dois idiomas possam ser traduzidos para frente e para trás, mas existem alguns programas nos dois idiomas que não podem ser traduzidos para o outro. Isso significa que nenhum idioma é estritamente mais expressivo que o outro, apenas possui recursos diferentes que permitem que diferentes programas sejam expressos de maneiras diferentes.
Isso fornece uma definição formal do que significa ser "mais expressivo", mas ainda não captura as noções psicológicas por trás do fenômeno. Por exemplo, o açúcar sintático, de acordo com esse modelo, não aumenta o poder expressivo de um idioma, porque pode ser traduzido usando apenas alterações locais. No entanto, sabemos por experiência que tem FOR
, WHILE
e IF
disponível, mesmo se eles são apenas açúcar sintático para condicionais GOTO
marcas expressar nossa intenção mais fácil .
O fato é que idiomas diferentes têm recursos diferentes que tornam mais fácil ou difícil expressar maneiras diferentes de pensar sobre um problema. E algumas pessoas podem encontrar uma maneira de expressar suas intenções mais facilmente e outras de uma maneira diferente.
Um exemplo que encontrei na tag Ruby no StackOverflow: muitos usuários que seguem a tag Ruby afirmam que os loops são mais fáceis de entender do que a recursão e a recursão é apenas para programadores funcionais avançados e os loops são mais intuitivos para os novatos, mas já vi vários casos de conclua os novatos que escrevem intuitivamente código como este:
def rock_paper_scissors
get_user_input
determine_outcome
print_winner
rock_paper_scissors # start from the top
end
O que geralmente leva várias pessoas a comentar que "isso não funciona" e "eles estão fazendo errado" e a "maneira correta" é esta:
def rock_paper_scissors
loop do
get_user_input
determine_outcome
print_winner
end
end
Portanto, claramente, existem pessoas para quem a recursão da cauda é uma maneira mais natural de expressar o conceito de "loop" do que as construções de loop.
Sumário
O fato de duas línguas serem equivalentes a Turing diz uma e exatamente uma coisa: que elas podem calcular o mesmo conjunto de funções em números naturais que uma máquina de Turing. É isso aí.
Não diz nada sobre a rapidez com que eles calculam essas funções. Não diz nada sobre a facilidade de expressar essas funções. E não diz nada sobre o que mais eles podem fazer além de computar funções em números naturais (por exemplo, vincular a bibliotecas C, ler entradas do usuário, gravar saída na tela).
isso significa que a classe de problemas que cada linguagem de programação pode resolver varia de acordo com a linguagem, mesmo que todas essas linguagens estejam completas?
Sim.
- Existem problemas que não são cobertos pelo termo "Turing-complete" (que se preocupa apenas com as funções de computação em números naturais), como imprimir na tela. Dois idiomas podem ser completos em Turing, mas um pode permitir a impressão na tela e o outro não.
- Mesmo que os dois idiomas possam resolver os mesmos problemas, isso não diz nada sobre a complexidade da codificação e como é fácil expressar essa codificação. Por exemplo, C pode resolver todos os problemas que Haskell pode, simplesmente escrevendo um intérprete Haskell em C ... mas você deve escrever o intérprete Haskell primeiro para resolver um problema dessa maneira!