Sommaire:
Quelle que soit la langue donnée, quelle est la plus petite quantité de caractères uniques pour que votre langue soit Turing-Complete ?
Défi:
Pour la langue de votre choix, recherchez le plus petit sous-ensemble de caractères permettant à votre langue d'être Turing-Complete. Vous pouvez réutiliser votre jeu de caractères autant de fois que vous le souhaitez.
Exemples:
JavaScript:
+!()[]
( http://www.jsfuck.com )Brainfuck:
+<>[]
(suppose une taille de cellule d'emballage)Python 2:
()+1cehrx
(fabriqué à partir de scripts commeexec(chr(1+1+1)+chr(1))
)
Notation:
Ce défi est marqué en caractères , pas en octets . Par exemple, les scores pour les exemples sont 6, 5 et 9.
Remarques:
Ce défi se différencie des autres en ce sens que votre langue est uniquement Turing-Complete (vous ne pouvez pas nécessairement utiliser toutes les fonctionnalités de la langue.)
Bien que vous puissiez le faire, veuillez ne pas poster de réponses sans réduire les caractères utilisés. Exemple: Brainfuck avec 8 caractères (car tous les autres caractères sont des commentaires par défaut.)
Vous DEVEZ au moins expliquer brièvement pourquoi votre sous-ensemble est Turing-Complete.
la source
Réponses:
Haskell, 4 caractères
Avec
()=
nous pouvons définir S, K et I. Les définitions doivent être séparées par un;
ou par un NL.Nous définissons
(==)
comme S (la deuxième ligne montre une version plus lisible):(===)
demander:et
(====)
comme je:Heureusement
(==)
,(===)
,(====)
, etc. sont fonction valides / noms de paramètres.Comme @ ais523 le souligne, SKI ne suffit pas dans un langage fortement typé comme Haskell, nous devons donc ajouter un combinateur de points fixes
(=====)
:la source
fix
SKI +, et que Turingfix
est complet, même dans un langage fortement typé.(==)
pour éviter tout conflit avec l'opérateur d'égalité par défaut(==)
, mais le code ci-dessus n'est qu'une preuve de son exhaustivité.JavaScript (ES6), 5 caractères
Merci à @ETHproductions et @ATaco pour leur aide dans ce domaine; Il s’agissait d’un projet de groupe et, bien que l’idée de départ soit la mienne, de nombreux détails leur appartiennent. Voir la discussion en ligne où ce sous-ensemble JavaScript a été développé ici .
Il est assez bien établi que tout programme Javascript peut être écrit avec les caractères (
[]()+!
), mais 5 caractères ne suffisent pas . Cependant, l'écriture de code JavaScript arbitraire n'est pas un défi. C'est un défi d'écrire un langage complet avec Turing et d'utiliser le fait que ces langages n'ont pas besoin d'accéder au DOM, ni même aux E / S interactives, il s'avère que nous pouvons écrire un programme avec toutes les fonctionnalités requises. , même sans aucune capacité à exécuter uneval
ou un équivalent.Amorçage de base
JavaScript est très flexible avec les types. Ainsi, par exemple,
[]
est un tableau vide, mais+[]
vaut 0 et[]+[]
représente la chaîne nulle. Notamment, le fait de pouvoir produire 0 avec ce jeu de caractères permet de simuler l’effet des parenthèses pour le regroupement;(a)
peut être écrit comme[a][+[]]
. Nous pouvons utiliser ce genre d’astuce pour produire différents personnages en utilisant seulement+[]
:[][+[]]
isundefined
(étant le premier élément d'un tableau vide); alors[]+[][+[]]
est"undefined"
(la stringification deundefined
); alors[[]+[][+[]]]
est["undefined"]
(envelopper cela dans un tableau); alors[[]+[][+[]]][+[]]
is"undefined"
(son premier élément); alors[[]+[][+[]]][+[]][+[]]
est"u"
(sa première lettre).u
est l’un des personnages les plus faciles à créer, mais des techniques similaires nous permettent de créer une gamme d’autres personnages. Le même lien que précédemment nous donne la liste suivante de caractères accessibles avec+[]
(c'est la même liste que pour+[]()
, à l'exclusion,,
car c'est la seule construction qui utilise des parenthèses à une fin autre que le regroupement / la priorité):Nous ne pouvons pas épeler beaucoup de mots utiles dans cet ensemble de caractères (rappelez-vous qu'il s'agit de l'ensemble des caractères que nous pouvons produire sous forme de chaînes ; nous ne pourrons pas les exécuter sans une sorte de
eval
). En tant que tel, nous avons besoin d'un autre personnage. Nous l'utilisons=
, car il sera utile plus tard, mais pour le moment, nous l'utilisons pour épeler l'opérateur de comparaison==
. Cela nous permet de produirefalse
ettrue
, ce qui peut être structuré et indexé, et nous permet d’ajouter immédiatementlrs
aux caractères que nous pouvons inclure dans des chaînes.De loin, le mot le plus important que cela nous permet d'épeler, que nous ne pouvions pas auparavant, est
constructor
. Maintenant, JavaScript a une syntaxe d'accès de propriété qui ressemble à ceci:mais vous pouvez aussi l'écrire comme ceci:
et rien ne nous empêche d'utiliser une propriété calculée, plutôt qu'un littéral de chaîne. Nous pouvons donc faire quelque chose dans le sens de
(avec les lettres générées comme décrit ci-dessus; le code devient rapidement très long!); c'est équivalent à
object.constructor
, ce qui nous permet d'accéder aux constructeurs d'objets arbitraires.Nous pouvons faire plusieurs astuces avec cela. Du banal au fantastique:
[[]+[]][+[]]["constructor"]
pour obtenir le constructeur d'une chaîne, dont le nom est String, puis le lustrer pour obtenir leS
caractère majuscule . Cela élargit un peu notre alphabet et nous aurons besoin de certains des nouveaux caractères plus tard.Tous les tableaux ont le même constructeur.
[]["constructor"] == []["constructor"]
esttrue
(contrairement à[] == []
ce qui est faux). Cela peut sembler mineur, mais c'est très important, car cela nous donne une méthode pour stocker les valeurs de manière persistante; nous pouvons définir une propriété aléatoire sur le constructeur et la relire plus tard. (C’est l’une des raisons pour lesquelles nous l’utilisons=
en particulier, plutôt que l’un des autres moyens de générertrue
etfalse
.) Par exemple, nous pouvons évaluer[[]["constructor"]["a"]=[]]
, puis lire[]["constructor"]["a"]
et récupérer le même tableau que nous y avons stocké.Cela répond à l' une des exigences dont nous avons besoin pour la complétude de Turing , à savoir la capacité de stocker et de récupérer des quantités arbitraires de données. Nous pouvons créer une cellule contre en utilisant un tableau à deux éléments, en prenant des valeurs de notre stockage de propriétés de constructeur, puis en le stockant à la place de l'une de ces valeurs, ce qui nous permet de construire des structures de données arbitrairement grandes en mémoire; et nous pouvons accéder à ce stockage en procédant à l’inverse, en le décomposant jusqu’à ce que les données souhaitées deviennent accessibles. La lecture est destructrice, mais cela est acceptable car nous disposons de plusieurs emplacements pour stocker les données. Nous pouvons donc les copier au fur et à mesure que nous les lisons, puis replacer la copie à l'emplacement d'origine.
Cela nous permet d’obtenir le constructeur d’une fonction (il existe de nombreuses fonctions
[]["find"]
auxquelles nous pouvons accéder avec notre alphabet limité; c’est-à-dire Array.find, c’est la plus accessible, mais il y en a d’autres). Pourquoi est-ce utile? Eh bien, nous pouvons en fait l’utiliser dans le but recherché pour un constructeur et construire des fonctions! Malheureusement, avec notre jeu de caractères, nous ne pouvons pas transmettre au constructeur de fonction une chaîne calculée. Cependant, l’utilisation de lui`
laisse un littéral de chaîne (par exemple[]["find"]["constructor"]`function body goes here`
); cela signifie que nous pouvons définir des valeurs personnalisées de type de fonction avec n'importe quel comportement à l'exécution, tant que nous pouvons exprimer ce comportement entièrement à l'aide de[]+=
. Par exemple,[]["find"]["constructor"]`[]+[]`
est une fonction assez ennuyeuse qui calcule la chaîne nulle, la supprime et quitte. cettela fonction n'est pas utile, mais les plus complexes le seront. Notez que, bien que les fonctions ne puissent prendre de paramètres ou renvoyer de valeurs, ce ne sont pas des problèmes pratiques car nous pouvons utiliser le stockage constructeur-propriété pour communiquer d'une fonction à une autre. Une autre restriction est que nous ne pouvons pas utiliser`
dans le corps d'une fonction.Nous pouvons maintenant définir des fonctions personnalisées, mais ce qui nous retient pour l’instant, c’est la difficulté que nous avons à les appeler . Au niveau supérieur du programme, nous pouvons appeler une fonction avec
``
, mais pouvoir appeler des fonctions uniquement à partir du niveau supérieur ne nous permet pas de faire une boucle. Nous avons plutôt besoin de fonctions pour pouvoir s’appeler.Nous accomplissons cela avec une astuce plutôt astucieuse. Rappelez-vous le capital que
S
nous avons généré plus tôt? Cela nous permet d'épeler"toString"
. Nous n'allons pas l'appeler; nous pouvons convertir des choses en chaînes en y ajoutant[]
. Nous allons plutôt le remplacer . Nous pouvons utiliser le stockage constructeur pour définir des tableaux persistants qui restent. Nous pouvons ensuite assigner les fonctions que nous avons créées auxtoString
méthodes des tableaux , et ces assignations resteront également fidèles. Maintenant, tout ce que nous avons à faire est un simple+[]
sur le tableau, et tout à coup, notre fonction personnalisée s’exécutera. Cela signifie que nous pouvons utiliser les caractères+=[]
appeler des fonctions, et donc nos fonctions peuvent s’appeler les unes les autres - ou elles-mêmes. Cela nous donne la récursion, ce qui nous donne des boucles, et tout à coup nous avons tout ce dont nous avons besoin pour la complétude de Turing.Voici un aperçu d'un ensemble de fonctionnalités qui donnent à Turing une complétude et comment elles sont implémentées:
if
et récursivité:if
: convertit un booléen en nombre et indexe dans un tableau à 2 éléments; un élément exécute la fonction pour lethen
cas où stringified, l'autre élément exécute la fonction pour leelse
cas quand stringified[a]+[b]+[c]
évaluea
,b
et dec
gauche à droite (au moins sur le navigateur que j'ai coché)Malheureusement, c'est assez peu pratique; non seulement il est énormément grand étant donné que les chaînes doivent être construites caractère par caractère à partir de principes premiers, il n'a également aucun moyen de faire des E / S (qui n'est pas obligé d'être complet de Turing). Toutefois, si elle se termine, il est au moins possible de rechercher ultérieurement dans le stockage du constructeur, de sorte qu'il n'est pas impossible de déboguer vos programmes, qui ne sont pas complètement non communicants.
la source
toString()
pour l'injection de dépendance était la manière la plus créative d'utiliser cette fonction. Maintenant j'ai changé d'avis.Unaire , 1 caractère
Le choix du personnage n'a pas vraiment d'importance; la longueur du programme définit le programme de brainfuck où il transpile. Bien que la spécification exige des
0
caractères, la plupart des transpileurs ne semblent pas vérifier cela.la source
vim,
9876 caractèresNous pouvons construire et exécuter un programme vimscript arbitraire comme suit:
Utilisez la séquence
aa<C-v><C-v>1<esc>dd@1<esc>dddd
pour obtenir un<C-a>
registre in1
.Passez en mode insertion avec
a
, puis insérez-en una
, qui sera utilisé pour revenir au mode insertion ultérieurement dans une macro.Pour chaque caractère du programme vimscript souhaité,
utiliser
<C-v><C-v>1<esc>
pour insérer la séquence littérale<C-v>1
,use
@1
(c'est-à-dire<C-a><cr>
, dans lequel la finale<cr>
est un no-op du fait d'être sur la dernière ligne) autant de fois que nécessaire pour incrémenter1
jusqu'à ce que la valeur ASCII du caractère souhaité soit atteinte,et revenez en mode insertion avec
a
.Supprimez la ligne (avec une nouvelle ligne) dans le
1
registre avec<esc>dd
.Exécutez le résultat sous forme de frappes vim en utilisant
@1
, puis<esc>dd
supprimez la ligne entrée par la nouvelle ligne de fin de l'étape précédente.Exécutez la séquence arbitraire résultante d'octets avec
dd@1
. S'il commence par un:
, il sera interprété comme du code vimscript et sera exécuté en raison du retour à la ligne de findd
.Je ne suis pas convaincu qu'il s'agisse d'un jeu de caractères minimal, mais il est assez facile de prouver que Turing est complet.
la source
i<C-v>1<ESC>
pas écrire<C-a>
, puisdd
pour pouvoir@1
incrémenter des nombres et éviter de devoir les utiliser<C-a>
?<C-v>10
insère un NUL plutôt que \ n (ne demandez pas). En tout cas, oui, peu importe la complétude de Turing.Perl, 5 caractères
Comme avec d'autres langages de script, l'idée est d'
eval
utiliser des chaînes arbitraires. Cependant, notre jeu de caractères n'inclut pas de guillemets ni d'opérateurs de concaténation. La construction de chaînes arbitraires sera donc beaucoup plus complexe. Notez que ceeval^"
serait beaucoup plus simple à gérer, mais a encore un caractère.Notre outil principal est
s<^><CODE>ee
, qui évalueCODE
, puis évalue sa sortie. Pluse
peut être ajouté, avec l'effet attendu.Nous obtenons des chaînes en utilisant
<>
, qui est l'opérateur glob sauf quand ce n'est pas le cas. Le premier caractère ne peut pas être<
(sinon il ressemble à l'<<
opérateur), les crochets doivent être équilibrés et il doit y avoir au moins un caractère autre que lettre (sinon, il est interprété comme l'opérateur readline).En xorisant ces chaînes ensemble, nous pouvons obtenir n'importe quelle combinaison de caractères
^B^V^S(*-/9;<>HJMOY[`\^begqstv
, à condition que nous acceptions de laisser des débris (les trois premiers sont des caractères de contrôle).Par exemple, supposons que nous voulons obtenir
"v99"
. Une façon d'obtenirv99
est"><<" ^ "e>>" ^ "see" ^ "^^^"
, mais nous ne pouvons pas représenter ces chaînes en raison des contraintes<>
. Au lieu de cela, nous utilisons:Les rendements ci-dessus
Y9;v99;
, qui, une fois évalués, donnent le même résultat qu’un simplev99
(à savoir, le caractère avec le code ASCII 99).Ainsi, nous pouvons utiliser tout le
^B^V^S(*-/9;<>HJMOY[`\^begqstv
jeu de caractères pour générer notre chaîne arbitraire, puis la convertir comme ci-dessus et la coller dans uns<><CODE>eeee
pour l'exécuter. Malheureusement, ce jeu de caractères est encore très limité, sans aucun moyen évident d'effectuer la concaténation.Mais heureusement, il contient l'étoile. Cela nous permet d’écrire
*b
, ce qui correspond à la chaîne"*main::b"
. Puis,*b^<^B[MMH^V^SY>
(^ B, ^ V et ^ S sont des caractères de contrôle littéral) nous donne(6, $&);
, qui, une fois encore eval-ed, renvoie la valeur de la variable de correspondance de Perl,$&
. Cela nous permet d'utiliser une forme limitée de concaténation: nous pouvons à plusieurs reprises ajouter des éléments à$_
utilisers<^><THINGS>e
, puis utilisers<\H*><*b^<^B[MMH^V^SY>>eee
pour évaluer$_
(\H
correspond à tout sauf aux espaces horizontaux; nous l'utilisons à la place du point, qui n'est pas dans notre jeu de caractères).En utilisant
9-/
, nous pouvons facilement générer tous les chiffres. En utilisant les chiffres,v
et la concaténation, nous pouvons générer des caractères arbitraires (vXXX donne le caractère avec le code ASCII XXX). Et nous pouvons concaténer ces éléments afin de générer des chaînes arbitraires. On dirait que nous pouvons faire n'importe quoi.Écrivons un exemple complet. Supposons que nous voulions un programme qui imprime son propre PID. Nous commençons avec le programme naturel:
Nous le convertissons en notation V:
Nous réécrivons ceci en utilisant uniquement
^B^V^S(*-/9;<>HJMOY[`\^begqstv
(les espaces sont uniquement pour la lisibilité et n’affectent pas la sortie):Enfin, nous convertissons le programme ci-dessus en only
<>^es
: pastebin . Malheureusement, cela bloque PerlExcessively long <> operator
, mais ce n’est qu’une limitation technique et ne doit pas être pris en compte.Ouf, c'était tout le voyage. Ce serait vraiment intéressant de voir quelqu'un proposer un jeu de 5 caractères qui simplifie les choses.
EDIT: en utilisant une approche légèrement différente, nous pouvons éviter de frapper la limite de longueur
<>
. Interprète brainfuck entièrement fonctionnel utilisant seulement<>^es
: Essayez-le en ligne! . Perl automatisé vers<>^es
transpiler: pastebin .la source
^
ou avec d’ autres personnages de base?Python 2, 7 caractères
Tout programme Python 2 peut être encodé en utilisant ces 7 caractères (
\n
est une nouvelle ligne).Construire des chaînes arbitraires
Nous pouvons effectuer la concaténation en appliquant de manière répétée l'opérateur de substitution
%
sur une seule chaîne. Par exemple, sia=1, b=2, c=3
,"%d%%d%%%%d" % a % b % c
nous donnera la chaîne"123"
. Heureusement, les lettresexec
nous donnent accès à%x
et%c
qui sont fondamentalementhex()
etchr()
. Avec%c
, nous pouvons construire n'importe quelle chaîne tant que nous avons les nombres nécessaires pour représenter les caractères. Nous pouvons ensuite exécuter cette chaîne en tant que code python en utilisant leexec
mot - clé.Faire des nombres
Nous pouvons avoir accès à
0
et1
dès le départ avec des comparaisons (==
). En combinant les chiffres concaténés et modulo, il est possible de construire le nombre43
qui représente+
en ASCII. Cela nous permet de construire les nombres dont nous avons besoin pour notre code.Mettre ensemble
J'ai omis plusieurs détails dans cette explication car ils ne sont pas essentiels pour comprendre comment les programmes soumis à ces contraintes peuvent être écrits. Voici un programme Python 2 que j’ai écrit et qui convertit n’importe quel programme python en une version fonctionnellement équivalente qui n’utilise que ces 7 caractères. Les techniques utilisées s'inspirent de cette soumission sur l'anarchie golf par k. Quelques astuces simples sont également utilisées pour maintenir la taille des programmes générés dans des limites raisonnables.
Essayez-le en ligne
la source
Mathematica,
5 à4 caractères
est un caractère Unicode à usage privé , qui agit en tant qu'opérateur pour vousFunction
permettre d'écrire des littéraux pour des fonctions non nommées avec des arguments nommés. Le caractère ressemble beaucoup↦
à Mathematica, je vais donc l'utiliser pour le reste de la réponse pour plus de clarté.L' utilisation de ces, nous pouvons mettre en œuvre le
S
,K
etI
combinateurs de la logique combinatoire:Un problème syntaxique avec ceux-ci est que la
↦
priorité est très faible, ce qui posera un problème si nous essayons de passer des arguments à ces combinateurs. Vous devriez normalement résoudre ce problème en plaçant un combinateurC
entre parenthèses(C)
, mais nous n'avons pas de parenthèses. Cependant, nous pouvons utiliserI
et[]
encapsulerC
une autre magie qui a suffisamment de préséance pour pouvoir l'utiliser ultérieurement:Enfin, pour écrire une application
A x y z
(oùA
est un combinateur « parenthesised » comme indiqué ci - dessus, etx
,y
,z
peuvent ou non être parenthesised, ou peut - être plus grandes expressions), nous pouvons écrire:Cela laisse la question de savoir comment l’équivalent parenthèse fonctionne réellement. Je vais essayer de l'expliquer en gros dans l'ordre dans lequel je l'ai trouvé.
Donc, ce que nous avons syntaxiquement pour grouper quelque chose, ce sont les crochets
[]
. Les crochets apparaissent de deux manières dans Mathematica. Soit en tant qu'invocations de fonctionf[x]
, soit en tant qu'opérateur d'indexationf[[i]]
(ce qui est vraiment juste un raccourci pourPart[f, i]
). En particulier, cela signifie que ni la syntaxe[C]
ni[[C]]
valide sont. Nous avons besoin de quelque chose devant. Cela peut théoriquement être n'importe quoi. Si nous utilisons le queI
nous avons déjà, nous pouvons obtenirI[C]
par exemple. Cela reste non évalué, car ceI
n'est pas une fonction unaire (ce n'est pas une fonction du tout).Mais maintenant, nous avons besoin d’un moyen d’extraire à
C
nouveau, car sinon, cela ne sera pas réellement évalué lorsque nous essaierons de lui passer un argumentx
.C’est là qu’il est pratique de
f[[i]]
pouvoir utiliser des expressions arbitrairesf
, pas seulement des listes. En supposant quef
lui - même est de la formehead[val1,...,valn]
, puisf[[0]]
donnehead
,f[[1]]
donneval1
,f[[-1]]
donnevaln
et ainsi de suite. Nous avons donc besoin d’obtenir l’un1
ou-1
de les extraire àC
nouveau, car l’unI[C][[1]]
ou l’I[C][[-1]]
évalueC
.Nous pouvons obtenir
1
un symbole arbitraire non défini commex
, mais pour ce faire, nous aurions besoin d'un autre caractère pour la division (x/x
donne1
pour non définix
). La multiplication est la seule opération arithmétique que nous puissions effectuer sans aucun caractère supplémentaire (en principe). Nous avons donc besoin d’une valeur pouvant être multipliée pour donner-1
ou1
. Cela finit par être la raison pour laquelle j'ai spécifiquement choisiI
nos identifiants. Parce queI
par lui-même est le symbole intégré de Mathematica pour l'unité imaginaire.Mais cela laisse un dernier problème: comment pouvons-nous réellement multiplier
I
par lui - même? Nous ne pouvons pas simplement écrireII
parce que cela est analysé comme un symbole unique. Nous devons séparer ces jetons sans a) changer leur valeur et b) en utilisant de nouveaux caractères.Le dernier élément magique est un élément de comportement non documenté:
f[[]]
(ou de manière équivalentePart[f]
) est une syntaxe valide et se retournef
. Donc, au lieu de multiplierI
parI
, nous multiplionsI[[]]
parI
. Lorsque vous insérez des crochets, Mathematica recherche ensuite un nouveau jeton etI[[]]I
évalue-1
si nécessaire. Et c'est comme ça qu'on se retrouveI[C][[I[[]]I]]
.Notez que nous ne pouvions pas utiliser
I[]
. Il s'agit d'une invocation sans argument de la fonctionI
, mais comme je l'ai dit précédemmentI
n'est pas une fonction, elle ne sera donc pas évaluée.la source
Python 2, 8 caractères
Ces caractères permettent la traduction / exécution de n’importe quel programme Python en utilisant des chaînes de format et
exec
. Bien que la capacité de traduire un programme ne soit pas nécessaire à la complétude de Turing, c'est le moins de personnages que je connaisse qui en font de toute façon TC. Que ce soit si puissant n'est qu'un bonus.Un guillemet double ainsi que tout chiffre autre que zéro pourraient également être utilisés. (Maintenant que je pense,
1
serait certainement mieux, ce qui dans les programmes plus courts, puisque vous pouvez utiliser1
,11
et111
, aussi bien.)Voici le programme
print
:Essayez-le en ligne
Le programme de base nécessite
Chaque caractère
x
ajouté au programme nécessite (nombre de caractères):%
-2**(n-1)
c
-1
-
-ord(x)*2
~
-ord(x)*2
0
-1
L'exception à cette règle est que certaines optimisations / raccourcis peuvent être utilisés
%'0'
pour raccourcir le programme codé, par exemple utiliser le caractère0
au lieu de 48-~
, etc.Utilisations pratiques (AKA golfing): J'ai utilisé cette tactique pour résoudre ce défi sans utiliser de handicap supplémentaire.
Crédit pour la liste de caractères et un programme de codeur: ici
Pour plus d'informations sur la recherche d'une limite inférieure sur la taille du programme résultant (sans optimisations), consultez ce commentaire .
Le nombre d'octets requis augmente
O(2**n)
, cette méthode n'est donc pas recommandée pour le golf. Un quine utilisant cette restriction de source serait incroyablement long.la source
+
ou-
avant le%
, nous pourrions supprimer un caractère.exec
toute façon.C (unportable),
24 1813 caractèresCela couvre tous les programmes de la forme
... où la séquence de constantes (de la forme 1 + 1 + 1 ...) contient la représentation du code machine de votre programme. Cela suppose que votre environnement autorise l'exécution de tous les segments de mémoire (apparemment vrai pour tcc [merci @Dennis!] Et pour certaines machines sans bit NX). Sinon, pour Linux et OSX, vous devrez peut-être ajouter le mot clé à la fin
const
et pour Windows, vous devrez peut-être ajouter#pragma
explicitement le segment comme exécutable.À titre d'exemple, le programme suivant écrit dans le style ci-dessus s'imprime
Hello, World!
sous Linux et OSX sous x86 et x86_64.Essayez-le en ligne!
la source
0==1111111111+1111111111+1111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+111+111+111+111+111+11+11+11+11+11+11+11+1
const
. tio.run/nexus/…1==11
Retina , 6 personnages
Ainsi que des sauts de ligne (0x0A).
D'un côté, je suis surpris de pouvoir l'avoir aussi bas. D'autre part, je suis très mécontent de l'inclusion de
%¶
. Chacune des$`{
est réutilisée pour deux voire trois buts, mais%¶
ensemble ne servent qu’un seul but. Cela les rend plutôt inutiles et détruit légèrement l’élégance de la démarche. J'espère qu'il y a un moyen de battre cette construction.Sur la preuve. Je vais décrire un moyen simple de traduire les systèmes de balises cycliques en rétine à l'aide des caractères ci-dessus.
Tout d'abord, nous utiliserons
`
et{
pour l'alphabet binaire au lieu de0
et1
. Celles-ci sont pratiques, car elles n'ont pas besoin d'être échappées dans une expression rationnelle, mais elles ont une signification pour Retina ou dans la syntaxe de substitution. J'utilise`
pour0
et{
pour1
, mais ce choix est arbitraire. De plus, nous allons inverser la chaîne (et les productions) en mémoire, parce que travailler avec le dernier caractère nous permet d' utiliser$
et au$`
lieu de^
et$'
, en maximisant la réutilisation des caractères.Si le mot initial est noté
S
et que la productioni
th (inversée) est appelée , le programme résultant ressemblera à ceci:pi
Cette construction grandit inévitablement en mémoire chaque fois qu'une production est appliquée, et il est peu probable qu'elle se termine - en fait, au moment où le système de balises cycliques prendrait fin (lorsque la chaîne de travail devient vide), le comportement du programme Retina résultant devient fondamentalement indéfini.
Regardons ce que le programme fait:
Nous commençons par initialiser la chaîne de travail au mot initial.
Cela enveloppe le reste du programme dans un programme qui s'exécute jusqu'à ce qu'il ne parvienne pas à changer la chaîne résultante (hé, détection naïve de la boucle infinie intégrée gratuite ...). Les deux sauts de ligne ne sont pas vraiment nécessaires, mais ils séparent plus clairement la boucle des productions individuelles. Le reste du programme passe en revue chacune des productions et, en raison de la boucle, nous les traitons effectivement de manière cyclique, encore et encore.
Chaque production est traitée en deux étapes. Nous traitons d’abord le cas où le symbole principal (ou, dans notre cas, le dernier symbole) est
{
, auquel cas nous utilisons la production:La regex ne correspond que si la chaîne se termine par
{
. Si tel est le cas, nous le remplaçons par:¶
). Nous ne travaillerons jamais qu'avec la dernière ligne de la chaîne de travail, de sorte que la chaîne de travail est jusqu'à présent ignorée (c'est pourquoi l'utilisation de la mémoire du programme augmentera de plus en plus).pi
$%`
). C'est pourquoi nous devons insérer le¶
:$%`
ramasse tout ce qui reste du match, mais uniquement sur la même ligne. Par conséquent, cela ne voit pas tous les déchets que nous avons laissés par les productions précédentes. Cette astuce nous permet de faire correspondre quelque chose à la fin de la chaîne de travail pour insérer quelque chose au début de la chaîne de travail, sans avoir à utiliser quelque chose du genre(.+)
et$1
qui aurait pour effet d'augmenter considérablement le nombre de caractères dont nous avons besoin.`
). Cela remplace efficacement le{
(1
-symbol) que nous avons associé à un`
(0
-symbol), de sorte que l'étape suivante n'a pas besoin de savoir si nous avons déjà traité la production en cours ou non.La deuxième partie de chaque production est alors le cas trivial où la production est ignorée:
Nous supprimons simplement une fin
`
. La raison pour laquelle nous avons besoin de deux`
sur la première ligne est que Retina considère que le premier backtick est le séparateur entre configuration et regex. Cela lui donne simplement une configuration vide afin que nous puissions utiliser des backticks dans la regex elle-même.la source
Java 7,
1817 caractères\bcdefu0123456789
Tout le code source Java peut être réduit à des points de code Unicode. "a" n'est pas nécessaire car il n'est utilisé que pour
*:jJzZ
. L'astérisque est utilisé pour la multiplication ou le blocage de commentaires. La multiplication est simplement une addition répétée et vous pouvez utiliser des commentaires sur une seule ligne (ou simplement les omettre). Les deux points sont utilisés pour les opérateurs ternaires, pour lesquels vous pouvez utiliser une instruction if, et pour les boucles foreach, qui peuvent être remplacés par des boucles normales pour. j et z ne font partie d'aucun mot clé en java.Pour supprimer tout autre caractère, nous devons ajouter au moins un des caractères requis dans la plaque de la chaudière Java
class a{public static void main(String[]a){}}
. Voir ci-dessous:Voici un exemple avec un programme Hello World Essayez-le en ligne!
Java 8, 16 caractères
\bdefu0123456789
Merci à ais523 de l'avoir signalé. Java 8 permet aux interfaces d'avoir des méthodes statiques, ce qui signifie que nous pouvons supprimer "c" car nous n'en avons pas besoin pour le "l" dans "class". "c" étant utilisé
,<lL\|
, nous finissons par perdre un peu plus de fonctionnalités java que lorsque nous avons supprimé "a", mais nous en avons encore suffisamment pour être complets. Essayez-le en ligne!la source
Java, 127 characters
... Sympa, Poke;)c
(toutes les lettresinterface
sont toujours accessibles avec aucuna
ouc
dans vos littéraux hexadécimaux).Labyrinthe , 5 personnages
Plus sauts de ligne (0x0A) et espaces (0x20).
Je vais esquisser une preuve sous la forme d'une réduction de Smallfuck (une variante de Brainfuck réduite utilisant des cellules à 1 bit). Notez que Smallfuck lui-même n’est pas Turing-complete car le langage spécifie que sa bande doit être finie, mais nous allons supposer une variante de Smallfuck avec une bande infinie, qui serait alors Turing-complete (Labyrinth n’a pas de mémoire). restrictions par conception).
Un invariant important tout au long de cette réduction sera que chaque programme (ou sous-programme) donnera un programme (ou sous-programme) Labyrinth qui commence et se termine sur la même ligne et couvre un nombre pair de colonnes.
Labyrinth a deux piles qui sont initialement remplies d'une quantité infinie (implicite) de
0
s.{
et}
décaler la valeur supérieure entre ces piles. Si nous considérons que le haut de la pile principale est la "cellule" actuelle, ces deux piles peuvent alors être considérées comme les deux moitiés semi-infinies de la bande infinie utilisée par Smallfuck. Cependant, il sera plus pratique d’avoir deux copies de chaque valeur de bande sur les piles, pour garantir l’invariant mentionné ci-dessus. Par conséquent<
,>
ils seront traduits en{{
et}}
, respectivement (vous pouvez les échanger si vous le souhaitez).Au lieu de permettre les valeurs de cellule
0
et1
, nous utilisons0
et-1
, avec lesquels nous pouvons basculer entre~
(négation au niveau du bit). Les valeurs exactes n’ont aucune importance pour la complétude de Turing. Nous devons changer les deux copies de la valeur sur la pile, ce qui nous donne à nouveau une traduction de longueur égale:*
devient~}~{
.Cela laisse les commandes de flux de contrôle
[]
. Labyrinth n'a pas de flux de contrôle explicite, mais le flux de contrôle est déterminé par la structure du code. Nous avons besoin des espaces et des sauts de ligne pour faire cette mise en page.Tout d’abord, notez que
~~
c’est un non-op, car les deux~
annulent effectivement. Nous pouvons utiliser cela pour avoir des chemins arbitrairement longs dans le code, tant que leur longueur est paire. Nous pouvons maintenant utiliser la construction suivante pour traduireAA[BB]CC
Labyrinth (j'utilise des lettres doubles pour que la taille de chaque extrait de Labyrinth soit égale, comme le garantit l'invariant):Ici,
..
est un nombre approprié~
qui correspond à la largeur deBB
.Encore une fois, notez que la largeur de la construction reste égale.
Nous pouvons maintenant passer en revue le fonctionnement de cette boucle. Le code est entré via le
AA
. Le premier~~
ne fait rien et nous permet d'atteindre la jonction. Cela correspond à peu près au[
:BB
. La..
partie est toujours un no-op. Ensuite, nous en atteignons un~
à un autre croisement. Nous savons maintenant que la valeur actuelle est non nulle et que l'IP prend alors le virage nord. Il fait le tour en haut, jusqu’à atteindre un autre croisement après six heures~
. Donc, à ce stade, la valeur actuelle est toujours non nulle et l’IP reprend son virage pour aller vers l’est en direction de laCC
. Notez que les trois~
précédant leCC
retour à la valeur actuelle0
, comme il se doit lorsque la boucle a été ignorée.~
avant d’atteindreBB
(ce qui ne fait rien), puis six autres~
avant d’atteindre la prochaine intersection. Cela correspond à peu près au]
.~
valeur suivante rend la valeur non nulle, de sorte que l'adresse IP utilise cette deuxième jonction, qui fusionne le chemin avec le cas où la boucle a été ignorée complètement. De nouveau, les trois~
renvoient la valeur à zéro avant d’atteindreCC
.~
avant la prochaine jonction, ce qui signifie qu'à ce stade, la valeur actuelle est égale à zéro, de sorte que l'adresse IP continue à aller vers l'ouest. Ensuite, il y aura un nombre impair~
avant que l'IP n'atteigne à nouveau la jonction initiale, de sorte que la valeur soit renvoyée-1
et que l'IP se déplace vers le sud à la prochaine itération.Si le programme contient des boucles, la toute première
AA
doit être étendue en haut du programme pour que l'IP trouve la bonne cellule sur laquelle démarrer:C'est ça. Notez que les programmes résultant de cette réduction ne se termineront jamais, mais cela ne fait pas partie des exigences de la complétude de Turing (considérez la règle 101 ou Fractran).
Enfin, il reste la question de savoir si cela est optimal. En termes de charge de travail, je doute qu'il soit possible de faire mieux que trois commandes. Je pouvais voir une construction alternative basée sur des machines Minsky avec deux registres, mais cela nécessiterait
=()
ou=-~
, ne disposant que d’une commande de manipulation de pile mais de deux commandes arithmétiques. Je serais heureux de me tromper à ce sujet. :)En ce qui concerne les commandes de présentation, je pense que les sauts de ligne sont nécessaires, car un contrôle utile est impossible sur une seule ligne. Cependant, les espaces ne sont pas techniquement nécessaires. En théorie, il pourrait être possible de concevoir une construction qui remplisse toute la grille avec
~{}
(ou=()
ou=-~
), ou utilise une disposition irrégulière où les lignes ne sont pas toutes de la même longueur. Cependant, écrire un code comme celui-là est incroyablement difficile, car Labyrinth traitera alors chaque cellule comme une jonction et vous devez faire très attention à ce que le code ne se ramifie pas lorsque vous ne le souhaitez pas. Si quelqu'un peut prouver ou réfuter s'il est possible d'omettre l'espace pour que Turing soit complet, je serais heureux de donner une prime substantielle pour cela. :)la source
Haskell,
57 caractèresEn tant que langage fonctionnel, Haskell a bien sûr des lambdas, il est donc facile de simuler le calcul lambda. La syntaxe pour lambdas est la suivante: nous avons au moins besoin des caractères . De plus, nous avons besoin d'un nombre illimité de symboles variables pour pouvoir construire des expressions lambda arbitraires. Heureusement , on n'a pas besoin de nouveaux personnages pour cela, parce que , , , ..., sont tous les noms de variables valides. En fait, chaque combinaison de parenthèses à l'intérieur est un nom de variable valide, à l'exception de just et , qui sont réservés aux expressions lambda, et qui commencent un commentaire de ligne.
(\
variable
->
body
)
argument
()\->
(>)
(>>)
(>>>)
\->
\
->
--
Exemples:
(\(>)(\\)(-)->(>)(-)((\\)(-)))
types à(t2 -> t -> t1) -> (t2 -> t) -> t2 -> t1
(\(>)(-)->(>))
types àt -> t1 -> t
(\(>)->(>))
types àt -> t
Edit: Cependant, comme l' a fait remarquer ais523 dans les commentaires, cette construction implémente le calcul lambda typé , qui en soi n'est pas complet, car il manque la capacité d'entrer dans des boucles infinies. Pour résoudre ce problème, nous avons besoin d’une fonction qui effectue la récursivité. Jusqu'ici, nous avons utilisé des lambdas anonymes, qui ne peuvent pas s'appeler eux-mêmes, car ils n'ont pas de nom. Il faut donc ajouter les personnages
=
et;
implémenter unefix
fonction:Avec cette déclaration, notre calcul lambda devient complet, bien que nous ayons ajouté
=
et;
, nous n’avons plus besoin de lambdas, comme vous pouvez le constater dans la réponse de nimi qui utilise justement()=;
.la source
main
?CJam, 3 personnages
Supprimé
)
selon la suggestion de Martin EnderSemblable à celui de Python donné à titre d'exemple.
En utilisant
'~
vous pouvez obtenir le~
personnage. Ensuite, en utilisant(
, vous pouvez le décrémenter pour obtenir le caractère de votre choix (~
le dernier caractère ASCII imprimable).~
évalue n'importe quelle chaîne en tant que code CJam normal. Les chaînes peuvent être construites en obtenant le caractère[
(par décrémentation~
), en l’évaluant, en insérant une séquence d’autres caractères, puis en évaluant le caractère]
. Grâce à cela, vous pouvez créer et exécuter n’importe quel programme CJam en utilisant uniquement ces trois caractères.Calculer 2 + 2 en utilisant seulement
'(~
la source
'~((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((~'~(((((((((((((((((((((((((((((((~'~(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((~
Brain-Flak , 6 personnages
Brain-Flak est un langage minimaliste avec seulement 8 caractères disponibles. Cependant, il peut être prouvé qu’il existe un sous-ensemble de Brain-Flak qui est également complet en utilisant seulement 6 caractères.
La première chose à faire est de mettre en œuvre une machine Minsky avec une seule pile de Brain-Flak. Si nous pouvons prouver qu'une machine Minsky est possible avec une seule pile, nous pouvons montrer que Brain-Flak est complète sans les
<>
ni plus[]
ni moins. Cela ne sauvegardera aucun personnage immédiatement, mais le sera ultérieurement lorsque nous montrerons que ce<...>
n'est pas nécessaire.Une machine de Minsky est un type d'automate complet de Turing qui a un nombre fini de registres non bornés et deux instructions:
Incrémenter le registre
Si la valeur est différente de zéro, sinon, transition vers une instruction spécifiée
Pour configurer une structure de goto dans Brain-Flak, nous pouvons utiliser l'extrait suivant:
Cela va décrémenter le compteur et courir
%s
si zéro. Un tas de ces chaînes nous permettra de mettre un numéro sur la pile qui indiquera quelle ligne nous voulons aller. Chacune d'elles décrémentera le haut de la pile, mais un seul exécutera le code.Nous l'utilisons comme enveloppe pour toutes nos instructions Minsky Machine.
Incrémenter un registre particulier est assez facile sans changer de pile. Cela peut être réalisé avec cette formule de chaîne:
Par exemple, pour incrémenter le 3ème registre, nous écririons le code suivant:
Il ne nous reste plus qu'à mettre en œuvre la deuxième opération. Vérifier si un nombre est égal à zéro est assez simple dans Brain-Flak:
ne s’exécutera que
%s
si le TOS est nul. Ainsi, nous pouvons faire notre deuxième opération.Puisque les machines Minsky sont complètes de Turing, Brain-Flak est également complet de Turing sans utiliser les opérations
<>
and[]
.Cependant , nous n'avons pas réduit encore le nombre de caractères car
<...>
et[...]
sont encore en cours d' utilisation. On peut remédier à cela avec une simple substitution. Depuis<...>
est réellement équivalent à[(...)]{}
dans tous les cas. Ainsi, Brain-Flak est complet sans l'utilisation des caractères<
et>
(plus tous les no-ops).la source
<...>
et[...]
sont toujours en cours d'utilisation." Cependant, vous n'avez pas supprimé[...]
. S'il-vous-plaît, réparez.[...]
vraiment nécessaire? Pousser 0 peut être fait au début avec({})
(mais cela repose sur une pile vide, il faudra donc soigneusement mélanger les 0). Le principal problème est de pouvoir descendre dans la pile sans accès à<...>
(ce qui ne peut plus être simulé)> <> , 3 caractères
> <> est faisable en 3 avec
1p-
, qui:p
fournit une réflexion en modifiant le code source 2D en plaçant des caractères dans la boîte à code. Avec1-
, vous pouvez placer n'importe quel nombre dans la pile puisque1-
soustrait un et111-1--
(x-(1-1-1) = x+1
) en ajoute un.Une fois que toutes les
1p-
commandes ont été exécutées, le pointeur d’instruction s’enroule, lui permettant d’exécuter le code "réel".Un exemple de programme qui calcule les nombres de Fibonacci (à partir de cette réponse ) est:
Essayez-le en ligne! Une fois que toutes les
1p-
commandes ont été exécutées, la boîte à code se présente comme suit:Sauf tout après la
v
première ligne, il s'agit d'un programme Fibonacci> <> standard.la source
bash, 9 caractères
Bash a une syntaxe
$'\nnn'
pour la saisie de caractères avec leurs valeurs ascii octales. Nous pouvons entrer laeval
commande dans ce format en tant que$'\145\166\141\154'
. Nous transformons d'abord le résultat souhaité en ses valeurs octales. Nous convertissons ensuite les valeurs octales en utilisant des chiffres autres que 0, 1, 4, 5 et 6 en expressions évaluant lesdites valeurs octales en utilisant une$(())
soustraction, en ajoutant uneval
avant. Dans notre dernière étape, nous en ajoutons un autreeval
et convertissons les parenthèses et le signe moins en leurs valeurs octales. En utilisant cette méthode, nous pouvons exécuter n’importe quelle commande bash, ce sous-ensemble est donc complet.Exemple:
dc
devient$'\144\143'
qui devient$'\145\166\141\154' \$\'\\144\\$((144-1))\'
qui devient$'\145\166\141\154' $'\145\166\141\154' $'\$\\\'\\\\144\\\\$\050\050144\0551\051\051\\\''
la source
Incident , 2 personnages
Peu importe les deux personnages que vous choisissez. toute combinaison de deux octets est Turing-complete dans Incident.
En fait , prouver ce qui est beaucoup plus difficile que vous pourriez vous attendre, et au moment de l' écriture, la page de discussion sur Esolang à propos de l' incident est consacré au problème. Je vais cependant essayer de résumer la preuve connue la plus simple ci-dessous.
Avant la preuve, un peu de fond. L'incident déduit les jetons utilisés dans le programme en examinant le source (un jeton est une chaîne qui apparaît exactement trois fois dans le source, n'est pas la sous-chaîne d'un autre jeton et ne chevauche pas un autre jeton potentiel). En tant que tel, n'importe quel programme peut être converti pour utiliser à peu près n'importe quel jeu de caractères en modifiant ce que sont les jetons; le langage est complet (et complet aussi pour les E / S!), bien qu’il soit incroyablement difficile à programmer, alors "tout" dont vous avez besoin est une méthode d’encodage des jetons pour qu’ils ne fonctionnent qu'avec deux caractères.
Et maintenant, voici un résumé de la preuve (qui a été trouvée par Ørjan, le mathématicien résident d'Esolang). L'idée est de coder un jeton en utilisant deux copies d'un caractère (par exemple
1
) dans une grande mer d'un autre caractère (par exemple0
). La distance entre le1
s est différent pour chaque jeton, mais est toujours un multiple de 4. Ensuite , pour le rembourrage entre les jetons, nous utilisons une liste supplémentaire de0
s avec1
au milieu, mais le nombre de 0s de chaque côté du1
est pas un multiple de 4, mais plutôt un nombre unique à cette incidence particulière du programme qui ne figure pas ailleurs dans le programme. Cela signifie que chacun1
…1
dans le remplissage ne peut apparaître que deux fois, alors ne fera pas partie d'un jeton; chaque jeton destiné contient exactement deux 1 et aucun jeton factice ne peut en contenir plus d'un1
. Ensuite, nous ajoutons simplement du rembourrage sur le côté pour supprimer tous les jetons possibles contenant un1
ou zéro1
s en en ajoutant au moins quatre copies.la source
Retina , 3 personnages
et newline.
Tout d’abord, nous avons besoin de newline pour pouvoir effectuer des substitutions (nécessaire à moins que nous ne voulions inclure tout le programme dans une regex, ce qui nécessiterait plus de caractères); et
`
et{
sont le moyen le moins intensif en caractères de faire des boucles. Il s'avère que nous n'avons besoin de rien d'autre.Notre langue cible à implémenter est une variante déterministe de Thue (le non déterminisme n'est pas nécessaire pour la complétude de Turing; il est possible d'écrire un programme Thue pour qu'il fonctionne correctement quel que soit l'ordre d'évaluation utilisé). L’idée de base est de compiler
pattern::=replacement
en(qui est une traduction directe du Thue par la rétine; alternativement, si vous connaissez la rétine mais pas le Thue, vous pouvez l’utiliser comme méthode d’apprentissage du fonctionnement de Thue); exceptionnellement, le tout premier motif est précédé de
{`
(afin de placer le programme entier dans une boucle; les programmes Thue continuent de s'exécuter jusqu'à ce qu'il n'y ait plus de substitutions possibles, ce qui entraîne le fonctionnement identique de la rétine).Bien sûr, cela signifie que nous devons prouver que Thue Turing est complet avec juste
{
et`
dans les modèles et le remplacement, mais c'est assez simple; nous remplaçons un caractère par le code ascii n avec`
, n +1{
, et un autre`
. Il est clairement impossible pour un motif de correspondre n'importe où, sauf aux limites du personnage. Cela finira donc par faire la même chose que le programme d'origine.la source
Brachylog , 5 personnages
Ce sous-ensemble de caractères nous permet d'implémenter une version de Fractran dans laquelle les seuls nombres pouvant apparaître sont les produits de repunits (c'est-à-dire les produits de nombres pouvant être écrits en décimal à l'aide du chiffre 1 uniquement).
~×
(avec un entier en indice) divise la valeur actuelle par cet entier, mais uniquement si elle est divisée exactement (sinon, elle "échoue" et recherche un autre cas à exécuter;|
sépare les cas).×
nous permet de multiplier par un entier. Donc, en utilisant~×₁|
nous pouvons implémenter une étape d’une exécution Fractran. Ensuite ,↰
nous permet RECURSE, l' exécution du programme tout à nouveau sur la nouvelle valeur actuelle. Voici un exemple de programme Fractran très simple (11111111111111111111111/111
) traduit en Brachylog.Alors, est-ce que Turing est complet? Tout ce dont nous avons besoin pour que Fractran Turing soit complet est une quantité suffisamment grande de nombres premiers (assez pour écrire un interprète pour une langue complète de Turing dans Fractran même). Il y a cinq preuves et quatre soupçonnésrepunit prime, en plus, peut-être, de ceux qui n’ont pas encore été découverts. C'est en fait plus que ce dont nous avons besoin dans ce cas. Le programme vérifie les possibilités de gauche à droite, nous pouvons donc utiliser un nombre premier comme pointeur d’instruction et deux autres comme compteurs, démontrant ainsi la complétude de Turing avec seulement trois nombres premiers (une bonne chose aussi, car cela nous permet d’utiliser les repunits avec 2, 19 , et 23 chiffres, sans avoir à recourir aux répétitions éprouvées mais ennuyeuses de 317 ou 1031 chiffres, ce qui rendrait le code source assez difficile à écrire). Cela permet d'implémenter une machine Minsky avec deux compteurs (assez pour la complétude de Turing).
Voici comment la compilation fonctionne spécifiquement. Nous utiliserons les deux commandes suivantes pour notre implémentation de machine Minsky (connue sous le nom de Turing complete), et chaque commande aura un entier comme étiquette:
Nous choisissons la commande à exécuter en plaçant des puissances de 11 au dénominateur, les plus hautes puissances en premier; l'exposant de 11 est l'étiquette de la commande. De cette façon, la première fraction qui correspond sera la commande en cours d’exécution (car les précédentes ne peuvent pas se diviser par tous ces 11). Dans le cas d'une commande de décrémentation, nous plaçons également un facteur de 1111111111111111111 ou 1111111111111111111111111 dans le dénominateur, pour le compteur A ou B respectivement, et nous le suivons avec une autre commande sans ce facteur; le cas "décrément" sera implémenté par la première commande, le cas "zéro" par la seconde. Pendant ce temps, le "goto" sera traité avec une puissance appropriée de 11 au numérateur et un "incrément" via un facteur de 1111111111111111111 ou 11111111111111111111111 au numérateur.
la source
Befunge-98, 3 caractères
Pour autant que je sache, Befunge-98 est supposé être complet, nous devons donc montrer comment tout programme Befunge-98 peut être généré en utilisant seulement trois caractères. Ma solution initiale reposait sur les quatre caractères suivants:
Nous pouvons obtenir n'importe quel entier positif sur la pile en ajoutant plusieurs
1
valeurs avec la+
commande, et pour zéro, nous utilisons simplement0
. Une fois que nous avons la capacité de composer le nombre que nous voulons, nous pouvons utiliser lep
commande (put) pour écrire toute valeur ASCII à n’importe quel emplacement du champ de jeu Befunge.Cependant, comme l' a souligné Sp3000 , vous ne pouvez vous débrouiller qu'avec ces trois caractères:
Tout nombre négatif peut être calculé en commençant par
1
puis en soustrayant plusieurs fois1
(par exemple, -3 serait11-1-1-1-
). Ensuite, tout nombre positif peut être représenté en soustrayant 1-n de 1, où 1-n est un nombre négatif que nous savons déjà manipuler (par exemple, 4 = 1 - (- 3), ce qui serait111-1-1-1--
).Nous pouvons donc utiliser nos trois caractères pour écrire un type de chargeur de démarrage qui génère lentement le code que nous voulons exécuter. Une fois que ce chargeur est terminé, il se termine au début de la première ligne du champ de jeu, qui devrait alors contenir le début de notre code nouvellement généré.
À titre d'exemple, voici un chargeur de démarrage qui génère le code Befunge nécessaire pour additionner 2 + 2 et générer le résultat:
22+.@
Et pour un exemple un peu plus compliqué, voici "Hello World":
"!dlroW olleH"bk,@
la source
1p-
aussi bienRuby, 8 caractères
Inspiré par les réponses Python
Comment ça fonctionne
Une chaîne en ruby peut être construite en utilisant la chaîne vide comme point de départ et en y ajoutant des caractères ascii, par exemple:
est en fait équivalent à
qui évalue la chaîne
la source
OCaml, 9 caractères
Ces caractères suffisent pour implémenter le calcul SKI Combinator dans OCaml. Nous pouvons notamment éviter l’utilisation d’espace avec des parenthèses suffisantes. Malheureusement, les expressions lambda dans OCaml nécessitent la
fun
mot clé. Une solution plus concise n’est donc pas possible. Les mêmes lettres peuvent être utilisées pour créer des noms de variables arbitraires si des expressions lambda plus complexes sont souhaitées.S combinateur:
fun(f)(u)(n)->f(n)(u(n))
avec le type('a -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'c
K Combinator:
fun(f)(u)->u
avec le type'a -> 'b -> 'b
Je Combinator:
fun(f)->f
avec le type'a -> 'a
Comme indiqué par ais523, il ne suffit pas de coder simplement SKI. Voici un codage pour Z utilisant des variantes polymorphes pour manipuler le système de types. Avec cela, mon sous-ensemble devrait être complet.
Z Combinator:
avec le type
(('a -> 'b) -> 'a -> 'b) -> 'a -> 'b
la source
Langages concaténatifs basés sur la pile, 4 caractères
Sous-charge
GolfScript
CJam
GS2
@
space (je savais que GS2 utilisait beaucoup d'imprimables, mais c'est ridicule…)dc (suggéré par @seshoumara)
La sous-charge a été prouvée Turing-complete avec seulement l'utilisation de
():^
(grâce au mathématicien résident d'Esolang Ørjan). La preuve est beaucoup trop longue à expliquer ici, mais si cela vous intéresse, vous pouvez lire à ce sujet ici .Les commandes en question sont
()
(placez le littéral de code sur la pile),:
(élément dupliqué de la pile supérieure) et^
(évaluez le sommet de la pile). Ces commandes sont assez courantes dans les langages basés sur des piles (en particulier les langages concaténatifs), aussi en ai-je donné ci-dessus une collection; ces langues sont toutes Turing-complete en 4 caractères pour la même raison que Underload.la source
Espaces blancs, 3 caractères
S
is space,T
is tab etL
newline.la source
Raquette (Scheme), 4 personnages
En utilisant uniquement λ, parenthèse et espace, nous pouvons directement programmer dans le sous-ensemble Lambda Calculus de Scheme. Nous réutilisons le caractère λ pour tous les identificateurs en les concaténant ensemble pour fournir un nombre arbitrairement grand d'identificateurs uniques.
À titre d'exemple, voici le combinateur classique Omega, qui boucle en boucle pour toujours.
la source
Python 3, 9 caractères
Voir ma réponse à Python 2 pour une explication de base. Cette réponse s'appuie sur celle-là.
Au lieu d'utiliser simplement les mêmes caractères que Python deux avec l'ajout de
()
, nous pouvons supprimer un caractère puisque nous utilisons maintenant des parenthèses. Les programmes auront toujours la forme de base demais nous raccourcissons la durée du programme en utilisant à la
+
place de-
, puis nous pouvons supprimer~
en utilisant à la1
place de0
. Nous pouvons ajouter1
,11
et111
pour obtenir les valeurs ASCII nécessaires.Le programme
print()
devient le suivant le plus court possible:Essayez-le en ligne
Vous pensez peut-être à vous-même, comment créer un octet NUL sans
0
? Ne crains rien, jeune sauterelle! car nous avons la capacité d’utiliser les%
mathématiques aussi, en créant zéro avec1%1
.la source
PHP 7, 6 caractères
L'idée est qu'il est possible d'exécuter du code arbitraire en utilisant la construction suivante:
eval
ne fonctionnerait pas ici, car il s'agit d'une construction de langage et ne peut pas être appelé à l'aide de fonctions variables.create_function
et le code pourrait être écrit comme une concaténation de XOR au niveau du bit de caractères disponibles:En utilisant
().;^
pour<charX_Y>
, nous pouvons obteniret quelques caractères non imprimables. Ce n'est pas suffisant, mais nous pouvons maintenant appeler
'eXp'()
et obtenir des caractères numériques également:Cela nous donne
1
,2
et3
(les autres caractères seront ignorés par XOR, si l'autre chaîne est longue d'un caractère). De().;^123
nous pouvons maintenant générer tout le charset ASCII.Essayez-le en ligne
la source
Pyke, 5 personnages
Ceci est capable de produire un nombre infiniment grand, de le transformer en chaîne et de l'évaluer ensuite en tant que code Pyke.
Explication du code:
0
- Ajouter 0 à la pile. Ceci est nécessaire pour commencer un numéroh
- Incrémenter le nombre précédent. En répétant cette opération un nombre de fois arbitraire, vous pouvez créer des nombres infiniment grands. Pyke supporte les bignums tels qu’ils sont écrits en Python, qui les utilise par défaut..C
- Transformez un nombre en chaîne en utilisant l'algorithme suivant: ( lien Github )À ce stade, nous pouvons créer une quantité arbitraire de chaînes et de nombres naturels dans Pyke avec des valeurs arbitraires. Les nombres peuvent être créés sous la forme correspondant à l'expression régulière
0(h)*
et les chaînes peuvent être créées avec0(h)*.C
. Ils peuvent être imbriqués les uns dans les autres pour créer un mélange arbitraire de chaînes et de nombres entiers.E
- évalue une chaîne en tant que code Pyke. Cela utilise le même environnement que le code Pyke en cours d’exécution, ce qui permet de partager des éléments tels que l’entrée.Tentative de preuve que Pyke est Turing Complete.
Une des manières les plus simples de montrer qu'une langue est complète est d'y implémenter Brainf * ck. C’est probablement beaucoup plus difficile dans Pyke que dans beaucoup d’autres langues, car ses opérations de liste et de dictionnaire sont pratiquement inexistantes en raison du manque de ressources dans la région où Pyke est conçu pour fonctionner: code-golf .
Tout d'abord, nous créons un interpréteur pour brainf * ck et l'encodons à l'aide de notre algorithme ci-dessus pour créer un nombre, puis exprimons ce nombre avec
0
eth
. Nous créons ensuite la chaîne contenant le code à exécuter de la même manière. Si nous devions en rester là, nous aurions la pile commeCela signifie que le code doit être dans la forme opposée car la pile Pyke est du premier entré dernier sorti.
Passons maintenant à la partie amusante: l'interpréteur brainf * ck avec 216 octets!
Essayez-le ici!
Si vous voulez essayer le code sous une forme semi-complète mais modifiable, essayez-le ici!
Pour convertir une chaîne en un nombre, vous pouvez utiliser le code Python suivant:
La solution (presque) finale peut être essayée ici!
Explication de l'interprète Brainf * ck
Tout d’abord, séparons le programme en plusieurs parties:
Un séjour sans faille
Un séjour sans faille
+-
Un séjour sans faille
,
:Un séjour sans faille
.
:Un séjour sans faille
<>
:Un séjour sans faille
[
:Un séjour sans faille
- Et
]
:Un séjour sans faille
la source
Empilé, 5 caractères
C'est étonnamment court. Si Stacked peut implémenter chacune des combinaisons SKI, c'est Turing Complete. Résumer:
I
combinator - la fonction d'identité.x -> x
K
combinator - la fonction constante.x -> y -> x
S
combinator - la fonction de substitution.(x, y, z) -> x(z)(y(z))
Je combine:
{!n}
Maintenant, pour les détails empilés.
{! ... }
est un n-lambda. C'est une fonction unaire dont l'argument est implicitementn
. Ensuite, la dernière expression est renvoyée par la fonction. Ainsi,{!n}
est une fonction qui prend un argumentn
et donnen
.K combinateur:
{!{:n}}
Maintenant,
{:...}
est une fonction qui ne prend aucun argument, et retourne...
. En combinant cela avec notre formation n-lambda, nous obtenons (en ajoutant des espaces pour plus de clarté):S combinateur:
{n!nn!nnn:nnn{!n}!nn!nnn{!n}!n!!}
Ok, ça a l'air un peu plus compliqué. Ainsi, un lambda prend des arguments, séparés par des caractères non identifiants. Ainsi, le lambda dans l'en-tête est équivalent à:
Ceci est un lambda qui prend trois arguments,
n
,nn
etnnn
. Remplaçons ces avecx
,y
etz
pour plus de clarté:Les deux ne
{!n}!
sont que des fonctions d'identité pour éviter à nouveau les espaces, où!
signifie "exécuter". Donc, encore une fois, en réduisant:Avec une explication:
Et par conséquent, c'est le combinateur S.
la source
{n nn nnn:nnn{!n}!nn!nnn{!n}!n!!}
contient des espaces.