Ce que vous demandez essentiellement, c’est la différence entre le pouvoir de calcul et ce que l’on appelle communément le pouvoir d’expression (ou simplement l’ expression ) d’un langage (ou d’un système de calcul).
Puissance de calcul
Le pouvoir de calcul fait référence aux types de problèmes que le langage peut traiter. La classe de puissance de calcul la plus connue est celle qui équivaut à une machine de Turing universelle . Il existe de nombreux autres systèmes de calcul, tels que les machines à accès aléatoire , le λ-calcul , le calculateur combinatoire SK , les fonctions µ-récursives , les WHILE
programmes et bien d'autres. Et il s'avère que tous peuvent se simuler, ce qui signifie qu'ils ont tous la même puissance de calcul.
Cela donne lieu à la thèse de Church-Turing (nommée d'après Alonzo Church qui a créé le λ-calcul et Alan Turing qui a créé la machine de Turing universelle). The Church-Turing-Thesis est une hypothèse sur la calculabilité à deux aspects:
- tous les systèmes informatiques capables de calcul général sont également puissants, et
- Un être humain qui suit un algorithme peut calculer exactement les fonctions qu'une machine de Turing (et donc n'importe lequel des autres systèmes) peut calculer.
La seconde est plus importante dans le domaine de la philosophie de l’esprit que dans la science informatique.
Cependant, la thèse de l'Église de Turing ne dit pas deux choses qui sont très pertinentes pour votre question:
- quelle est l' efficacité des différentes simulations et
- comment pratique le codage d'un problème.
Un exemple simple pour (1): sur un ordinateur à accès aléatoire, la copie d'un tableau prend du temps proportionnellement à sa longueur. Sur une machine Turing, cependant, cela prend du temps proportionnellement au carré de la longueur de la matrice, car la machine Turing n’a pas d’accès mémoire aléatoire, elle ne peut se déplacer sur la bande, cellule par cellule. Par conséquent, il doit se déplacer n fois sur les n éléments du tableau pour les copier. Ainsi, différents modèles de calcul peuvent avoir différentes caractéristiques de performance, même dans le cas asymptotique, où nous essayons d’abstraire les détails de la mise en oeuvre.
Les exemples pour (2) abondent: λ-calcul et Python sont tous deux Turing-complete. Mais préférez-vous écrire un programme en Python ou en λ-calcul?
J'ai également contourné une troisième ride jusqu'à présent: tous ces systèmes originaux ont été conçus par des logiciens, des philosophes ou des mathématiciens, et non par des informaticiens… simplement parce que les ordinateurs et donc l'informatique n'existaient pas. Tout cela remonte au début des années 1930, avant même les toutes premières expériences de Konrad Zuse (qui n'étaient de toute façon pas programmables et / ou Turing complètes). Ils ne parlent que de "fonctions calculables sur les nombres naturels".
Il se trouve qu’il ya beaucoup de choses que vous pouvez exprimer comme fonctions sur les nombres naturels - après tout, nos ordinateurs modernes se débrouillent même avec beaucoup moins que cela (essentiellement 3-4 fonctions sur les nombres 0 et 1, et c’est tout ), mais par exemple, quelle fonction un système d’exploitation calcule-t-il?
Cette notion d'E / S, d'effets secondaires, en interaction avec l'environnement, n'est pas capturée par l'idée de "fonctions sur des nombres naturels". Et pourtant, il est assez important, puisque, comme Simon Peyton Jones a dit une fois « Toute une fonction pure, sans effets secondaires ne, est de rendre votre CPU chaud » , auquel un membre du public a répondu « En fait, c'est un côté -Effet aussi! "
Edwin Brady , le concepteur d’ Idris , (seulement la moitié) utilise en plaisantant (je ne sais pas s’il l’a inventé) le terme "Tetris-complete" pour exprimer cette différence entre "peut calculer toute fonction calculable sur des nombres naturels" et "peut être utilisé pour écrire des programmes non-triviaux qui interagissent avec l'environnement ". Encore plus ironiquement, il le démontre en mettant en œuvre un clone de Space Invaders dans Idris , mais il se dit confiant que Tetris se réduit à Space Invaders.
Une autre chose à souligner est que non seulement l'équivalence de Turing n'est pas nécessairement suffisante pour parler d'écriture de programmes "utiles", elle peut aussi ne pas être nécessaire . Par exemple, SQL n'est devenu équivalent à Turing qu'avec ANSI SQL: 1999 , mais il était encore utile auparavant. En fait, certains pourraient dire que le fait de rendre l'équivalent de Turing n'a pas du tout augmenté son utilité. Il existe de nombreux langages spécifiques à un domaine qui ne sont pas équivalents à Turing. Le langage de description de données n'est généralement pas (et ne devrait pas l'être). Total Languages ne peut évidemment pas être équivalent à Turing, mais vous pouvez toujours y écrire des boucles d’événements, des serveurs Web ou des systèmes d’exploitation. Il existe également des langues équivalentes à Turing, mais considérées comme une erreur.
Donc, dans l’ensemble, l’équivalence de Turing n’est pas très intéressante, à moins que vous ne souhaitiez analyser statiquement des programmes.
Expressivité
En supposant que notre système de calcul soit assez puissant pour résoudre même notre problème, nous devons ensuite exprimer notre algorithme de résolution du problème dans une sorte de notation formelle pour ce système. En d'autres termes: nous devons écrire un programme dans un langage informatique. C'est là qu'intervient la notion d' expressivité .
Il s'agit essentiellement de la "facilité" ou du "plaisir" d'écrire notre programme dans notre langage de programmation particulier. Comme vous pouvez le constater, la notion est assez vague, subjective et plus psychologique que technique.
Cependant, il existe des tentatives de définitions plus précises. Le plus célèbre (et le plus rigoureux que je connaisse) est de Matthias Felleisen dans son article Sur le pouvoir expressif des langages de programmation (les deux premières pages contiennent une introduction douce, le reste du document est plus charnu).
L’intuition principale est la suivante: lors de la traduction d’un programme d’une langue à une autre, certains des changements que vous devez faire sont localisés (par exemple, la conversion de FOR
boucles en WHILE
boucles ou de boucles en conditionnelles GOTO
), et certaines nécessitent un changement de structure du programme.
Lorsque vous pouvez remplacer une caractéristique d'une langue par une autre, d'une transformation différente, par des transformations locales uniquement, ces fonctionnalités sont réputées n'avoir aucun effet sur le pouvoir d'expression. C'est ce qu'on appelle le sucre syntaxique .
D'autre part, si cela nécessite un changement de la structure globale du programme, la langue dans laquelle vous traduisez est dite incapable d'exprimer la fonctionnalité. Et la langue à partir de laquelle vous traduisez est dite plus expressive (en ce qui concerne cette fonctionnalité).
Notez que cela donne une définition de l'expressivité objectivement mesurable. Notez également que la notion dépend du contexte de la fonctionnalité et qu'elle est comparative. Ainsi, si chaque programme de la langue A peut être traduit en langue B avec uniquement des modifications locales et s'il existe au moins un programme en langue B qui ne peut pas être traduit en A avec des modifications locales uniquement, la langue B est strictement plus expressive que la langue. UNE. Cependant, le scénario le plus probable est que de nombreux programmes dans les deux langues peuvent être traduits, mais il existe certains programmes dans les deux langues qui ne peuvent pas être traduits dans une autre. Cela signifie qu’aucun langage n’est strictement plus expressif que l’autre, ils ont juste des caractéristiques différentes qui permettent à différents programmes de s’exprimer de différentes manières.
Cela donne une définition formelle de ce que signifie être "plus expressif", mais ne capture toujours pas les notions psychologiques à la base du phénomène. Par exemple, le sucre syntaxique, selon ce modèle, n'augmente pas le pouvoir d'expression d'une langue, car il peut être traduit en utilisant uniquement des modifications locales. Cependant, nous savons par expérience que ayant FOR
, WHILE
et IF
disponibles, même si elles ne sont que du sucre syntaxique pour conditionnel GOTO
marques exprimant notre intention plus facile .
Le fait est que différentes langues ont des caractéristiques différentes qui permettent d’exprimer différentes façons de penser à un problème plus facilement ou plus difficilement. Et certaines personnes trouveront peut-être un moyen d’exprimer leur intention plus facilement et d’autres, d’une manière différente.
Un exemple que j’ai trouvé dans la balise Ruby sur StackOverflow: de nombreux utilisateurs qui suivent la balise Ruby affirment que les boucles sont plus faciles à comprendre que la récursion et que la récursivité n’est que pour les programmeurs fonctionnels avancés et que les boucles sont plus intuitives pour les nouveaux arrivants, mais j’ai vu plusieurs cas de les nouveaux venus qui écrivent intuitivement un code comme celui-ci:
def rock_paper_scissors
get_user_input
determine_outcome
print_winner
rock_paper_scissors # start from the top
end
Ce qui conduit généralement plusieurs personnes à commenter que "cela ne fonctionne pas" et "elles le font mal" et que la "manière correcte" est la suivante:
def rock_paper_scissors
loop do
get_user_input
determine_outcome
print_winner
end
end
Il est donc clair que, pour certaines personnes, la récursion de la queue est un moyen plus naturel d’exprimer le concept de "bouclage" que les constructions en boucle.
Sommaire
Le fait que deux langues soient équivalentes à Turing en dit long sur le fait qu’elles peuvent calculer le même ensemble de fonctions sur des nombres naturels qu’une machine de Turing. C'est ça.
Cela ne dit rien sur la rapidité avec laquelle ils calculent ces fonctions. Cela ne dit rien sur la facilité à exprimer ces fonctions. Et cela ne dit rien sur ce qu’ils peuvent faire en plus des fonctions de calcul sur les nombres naturels (par exemple, la liaison à des bibliothèques C, la lecture des entrées de l’utilisateur, l’écriture de la sortie sur l’écran).
Cela signifie-t-il que la classe de problèmes pouvant être résolus par chaque langage de programmation varie d’un langage à l’autre, même si ces langages sont tous terminés?
Oui.
- Il existe des problèmes qui ne sont pas couverts par le terme "Turing-complete" (qui ne concerne que les fonctions de calcul sur des nombres naturels), tels que l'impression à l'écran. Deux langues peuvent être complètes, mais l’une peut permettre l’impression à l’écran et l’autre pas.
- Même si les deux langues peuvent résoudre les mêmes problèmes, cela n’indique en rien la complexité de l’encodage et la facilité avec laquelle il peut être exprimé. Par exemple, C peut résoudre tous les problèmes que Haskell peut résoudre, simplement en écrivant un interprète Haskell en C… mais vous devez écrire d'abord l'interprète Haskell pour résoudre un problème de cette façon!