Mini-Flak, 6851113 cycles
Le programme (littéralement)
Je sais que la plupart des gens ne s'attendent pas à ce qu'un quine Mini-Flak utilise des caractères non imprimables et même des caractères multi-octets (ce qui rend l'encodage pertinent). Cependant, ce quine fait, et les non imprimables, combinés à la taille du quine (93919 caractères encodés en 102646 octets UTF-8), rendent assez difficile de placer le programme dans ce message.
Cependant, le programme est très répétitif et, en tant que tel, se comprime très bien. Pour que l'ensemble du programme soit disponible littéralement à partir de Stack Exchange, il existe un xxd
vidage hexadécimal réversible d'une gzip
version compressée du quine complet caché derrière le pliable ci-dessous:
00000000: 1f8b 0808 bea3 045c 0203 7175 696e 652e .......\..quine.
00000010: 6d69 6e69 666c 616b 00ed d9db 6a13 4118 miniflak....j.A.
00000020: 0060 2f8b f808 0d64 a1c1 1dc8 4202 c973 .`/....d....B..s
00000030: 4829 4524 0409 22e2 5529 a194 1242 1129 H)E$..".U)...B.)
00000040: d2d7 ca93 f9cf 4c4c d45b 9536 e6db 6967 ......LL.[.6..ig
00000050: 770e 3bc9 ffed eca9 edb7 b1a4 9ad2 6a1d w.;...........j.
00000060: bfab 75db c6c6 6c5f 3d4f a5a6 8da6 dcd8 ..u...l_=O......
00000070: 465b d4a5 5a28 4bd9 719d 727b aa79 f9c9 F[..Z(K.q.r{.y..
00000080: 43b6 b9d7 8b17 cd45 7f79 d3f4 fb65 7519 C......E.y...eu.
00000090: 59ac 9a65 bfdf 8f86 e6b2 69a2 bc5c 4675 Y..e......i..\Fu
000000a0: d4e4 bcd9 5637 17b9 7099 9b73 7dd3 fcb2 ....V7..p..s}...
000000b0: 4773 b9bc e9bd b9ba 3eed 9df7 aeaf 229d Gs......>.....".
000000c0: e6ed 5eae 3aef 9d46 21b2 5e4d bd28 942e ..^.:..F!.^M.(..
000000d0: 6917 d71f a6bf 348c 819f 6260 dfd9 77fe i.....4...b`..w.
000000e0: df86 3e84 74e4 e19b b70e 9af0 111c fa0d ..>.t...........
000000f0: d29c 75ab 21e3 71d7 77f6 9d8f f902 6db2 ..u.!.q.w.....m.
00000100: b8e1 0adf e9e0 9009 1f81 f011 18d8 1b33 ...............3
00000110: 72af 762e aac2 4760 6003 1bd8 698c c043 r.v...G``...i..C
00000120: 8879 6bde 9245 207c 04ae 5ce6 2d02 e1bb .yk..E |..\.-...
00000130: 7291 4540 57f8 fe0d 6546 f89b a70b 8da9 r.E@W...eF......
00000140: f5e7 03ff 8b8f 3ad6 a367 d60b f980 679d ......:..g....g.
00000150: d3d6 1c16 f2ff a767 e608 57c8 c27d c697 .......g..W..}..
00000160: 4207 c140 9e47 9d57 2e50 6e8e c215 b270 B..@.G.W.Pn....p
00000170: bdf6 9926 9e47 9d05 ce02 0ff0 5ea7 109a ...&.G......^...
00000180: 8ba6 b5db 880b 970b 9749 2864 47d8 1b92 .........I(dG...
00000190: 39e7 9aec 8f0e 9e93 117a 6773 b710 ae53 9........zgs...S
000001a0: cd01 17ee b30e d9c1 15e6 6186 7a5c dc26 ..........a.z\.&
000001b0: 9750 1d51 610a d594 10ea f3be 4b7a 2c37 .P.Qa.......Kz,7
000001c0: 2f85 7a14 8fc4 a696 304d 4bdf c143 8db3 /.z.....0MK..C..
000001d0: d785 8a96 3085 2acc 274a a358 c635 8d37 ....0.*.'J.X.5.7
000001e0: 5f37 0f25 8ff5 6854 4a1f f6ad 1fc7 dbba _7.%..hTJ.......
000001f0: 51ed 517b 8da2 4b34 8d77 e5b2 ec46 7a18 Q.Q{..K4.w...Fz.
00000200: ffe8 3ade 6fed b2f2 99a3 bae3 c949 9ab5 ..:.o........I..
00000210: ab75 d897 d53c b258 a555 1b07 63d6 a679 .u...<.X.U..c..y
00000220: 4a51 5ead a23a 6a72 9eb6 d569 960b f3dc JQ^..:jr...i....
00000230: 9ceb 53fa 658f 345f ad07 6f6f efce 06ef ..S.e.4_..oo....
00000240: 0677 b791 cef2 f620 57bd 1b9c 4521 b241 .w..... W...E!.A
00000250: 4d83 2894 2eaf a140 8102 050a 1428 50a0 M.(....@.....(P.
00000260: 4081 0205 0a14 2850 a040 8102 050a 1428 @.....(P.@.....(
00000270: 50a0 4081 0205 0a14 2850 a040 8102 050a P.@.....(P.@....
00000280: 1428 50a0 4081 0205 0a14 2850 a040 8102 .(P.@.....(P.@..
00000290: 050a 1428 50a0 4081 0205 0a14 2850 a040 ...(P.@.....(P.@
000002a0: 8102 050a 1428 50a0 4081 0205 0a14 2850 .....(P.@.....(P
000002b0: a040 8102 050a 1428 50a0 4081 0205 0a14 .@.....(P.@.....
000002c0: 2850 a040 8102 050a 1428 50a0 4081 0205 (P.@.....(P.@...
000002d0: 0a14 2850 a040 8102 050a 1428 50a0 4081 ..(P.@.....(P.@.
000002e0: 0205 0a14 2850 a040 8102 050a 1428 50a0 ....(P.@.....(P.
000002f0: 4081 0205 0a14 2850 a040 8102 050a 1428 @.....(P.@.....(
00000300: 50a0 4081 0205 0a14 2850 a040 8102 050a P.@.....(P.@....
00000310: 1428 50a0 4081 0205 0a14 2850 a040 8102 .(P.@.....(P.@..
00000320: 050a 1428 50a0 4081 0205 0a14 2850 a040 ...(P.@.....(P.@
00000330: 8102 050a 1428 50a0 4081 0205 0a14 2850 .....(P.@.....(P
00000340: a040 8102 050a 1428 50a0 4081 0205 0a14 .@.....(P.@.....
00000350: 2850 a040 8102 050a 1428 50a0 4081 0205 (P.@.....(P.@...
00000360: 0a14 2850 a040 8102 050a 1428 50a0 4081 ..(P.@.....(P.@.
00000370: 0205 0a14 2850 a01c 14ca 7012 cbb4 a6e9 ....(P....p.....
00000380: e6db e6b1 e4b1 9e4c 4ae9 d3be f5f3 745b .......LJ.....t[
00000390: 37a9 3d6a af49 7489 a6e9 ae5c 96dd 488f 7.=j.It....\..H.
000003a0: d31f 5da7 fbad 5d56 3e73 5277 7cf5 aa7b ..]...]V>sRw|..{
000003b0: 3fbc df7c e986 c3ba 5ee4 3c6f 74f7 c3e1 ?..|....^.<ot...
000003c0: 301a bb45 d795 9afb fbdc 1495 65d5 6d9b 0..E........e.m.
000003d0: baf7 a5b4 a87d 4a5b d7fd b667 b788 ec27 .....}J[...g...'
000003e0: c5d8 28bc b96a 9eda 7a50 524d 290a a5cb ..(..j..zPRM)...
000003f0: cbef 38cb c3ad f690 0100 ..8.......
(Oui, c'est tellement répétitif que vous pouvez même voir les répétitions après avoir été compressé).
La question dit "Je recommanderais également fortement de ne pas exécuter votre programme dans TIO. Non seulement TIO est plus lent que l'interpréteur de bureau, mais il expirera également dans environ une minute. Ce serait extrêmement impressionnant si quelqu'un réussissait à obtenir un score suffisamment bas pour s'exécuter. leur programme avant TIO expiré. " Je peux le faire! Il faut environ 20 secondes pour s'exécuter sur TIO, en utilisant l'interpréteur Ruby: Essayez-le en ligne!
Le programme (lisible)
Maintenant que j'ai donné une version du programme que les ordinateurs peuvent lire, essayons une version que les humains peuvent lire. J'ai converti les octets qui composent le quine en page de code 437 (s'ils ont le bit élevé défini) ou en images de contrôle Unicode (si ce sont des codes de contrôle ASCII), ajouté des espaces blancs (tout espace blanc préexistant a été converti en images de contrôle ), codé en fonction de la longueur d'exécution à l'aide de la syntaxe «string×length»
, et certains bits lourds en données ont été supprimés:
␠
(((()()()()){}))
{{}
(({})[(()()()())])
(({})(
{{}{}((()[()]))}{}
(((((((({})){}){}{})){}{}){}){}())
{
({}(
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45»
… much more data encoded the same way …
(«()×117»(«()×115»(«()×117»
«000010101011┬â┬ … many more comment characters … ┬â0┬â┬à00␈␈
)[({})(
([({})]({}{}))
{
((()[()]))
}{}
{
{
({}(((({}())[()])))[{}()])
}{}
(({}))
((()[()]))
}{}
)]{}
%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'×almost 241»
,444454545455┬ç┬ … many more comment characters … -a--┬ü␡┬ü-a␡┬ü
)[{}()])
}{}
{}({}())
)[{}])
(({})(()()()()){})
}{}{}␊
(Le "presque 241" est dû au fait que la 241e copie n'a pas de fin '
, mais est par ailleurs identique aux 240 autres.)
Explication
À propos des commentaires
La première chose à expliquer est, que se passe-t-il avec les caractères non imprimables et autres ordures qui ne sont pas des commandes Mini-Flak? Vous pensez peut-être que l'ajout de commentaires au quine rend les choses plus difficiles, mais c'est une compétition de vitesse (pas une compétition de taille), ce qui signifie que les commentaires ne nuisent pas à la vitesse du programme. Pendant ce temps, Brain-Flak, et donc Mini-Flak, vident simplement le contenu de la pile vers la sortie standard; si vous deviez vous assurer que la pile ne contenait queles personnages qui composaient les commandes de votre programme, vous auriez à passer des cycles à nettoyer la pile. En l'état, Brain-Flak ignore la plupart des caractères, aussi longtemps que nous nous assurons que les éléments de la pile de courrier indésirable ne sont pas des commandes Brain-Flak valides (ce qui en fait un polyglotte Brain-Flak / Mini-Flak), et ne sont pas négatifs ou extérieurs la plage Unicode, nous pouvons simplement les laisser sur la pile, leur permettre d'être sortis, et mettre le même caractère dans notre programme au même endroit pour conserver la propriété quine.
Il existe un moyen particulièrement important de tirer parti de cela. Le quine fonctionne en utilisant une longue chaîne de données, et fondamentalement toute la sortie du quine est produite en formatant la chaîne de données de différentes manières. Il n'y a qu'une seule chaîne de données, malgré le fait que le programme a plusieurs morceaux; nous devons donc pouvoir utiliser la même chaîne de données pour imprimer différentes parties du programme. L'astuce «données indésirables n'a pas d'importance» nous permet de le faire de manière très simple; nous stockons les caractères qui composent le programme dans la chaîne de données en ajoutant ou en soustrayant une valeur à ou de leur code ASCII. Plus précisément, les caractères composant le début du programme sont stockés en tant que leur code ASCII + 4, les caractères constituant la section qui est répétée près de 241 fois en tant que leur code ASCII - 4,chaque caractère de la chaîne de données avec un décalage; si, par exemple, nous l'imprimons avec 4 ajoutés à chaque code de caractère, nous obtenons une répétition de la section répétée, avec quelques commentaires avant et après. (Ces commentaires sont simplement les autres sections du programme, avec des codes de caractères décalés afin qu'ils ne forment pas de commandes Brain-Flak valides, car le mauvais décalage a été ajouté. Nous devons esquiver les commandes Brain-Flak, pas seulement les mini- Commandes Flak, pour éviter de violer la partie source restreinte de la question; le choix des décalages a été conçu pour garantir cela.)
En raison de cette astuce de commentaire, nous devons en fait uniquement être en mesure de sortir la chaîne de données formatée de deux manières différentes: a) codées de la même manière que dans la source, b) sous forme de codes de caractères avec un décalage spécifié ajouté à chaque code. C'est une énorme simplification qui fait que la longueur ajoutée en vaut vraiment la peine.
Structure du programme
Ce programme se compose de quatre parties: l'intro, la chaîne de données, le formateur de chaîne de données et l'outro. L'intro et l'outro sont essentiellement responsables de l'exécution de la chaîne de données et de son formateur en boucle, en spécifiant à chaque fois le format approprié (c'est-à-dire s'il faut encoder ou compenser, et quel décalage utiliser). La chaîne de données n'est que des données et est la seule partie du quine pour laquelle les caractères qui la composent ne sont pas spécifiés littéralement dans la chaîne de données (cela serait évidemment impossible, car cela devrait être plus long que lui-même); il est donc écrit d'une manière particulièrement facile à régénérer à partir de lui-même. Le formateur de chaîne de données est composé de 241 parties presque identiques, chacune formatant une donnée spécifique à partir des 241 de la chaîne de données.
Chaque partie du programme peut être produite via la chaîne de données et son formateur comme suit:
- Pour produire l'outro, formatez la chaîne de données avec le décalage +8
- Pour produire le formateur de chaîne de données, formatez la chaîne de données avec un décalage +4, 241 fois
- Pour produire la chaîne de données, formatez la chaîne de données via l'encodage dans le format source
- Pour produire l'intro, formatez la chaîne de données avec le décalage -4
Donc, tout ce que nous avons à faire est de voir comment ces parties du programme fonctionnent.
La chaîne de données
(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45» …
Nous avons besoin d'un encodage simple pour la chaîne de données car nous devons pouvoir inverser l'encodage en code Mini-Flak. Vous ne pouvez pas être beaucoup plus simple que cela!
L'idée clé derrière ce quine (en dehors de l'astuce de commentaire) est de noter qu'il n'y a fondamentalement qu'un seul endroit où nous pouvons stocker de grandes quantités de données: les «sommes des valeurs de retour de commande» dans les différents niveaux d'imbrication de la source du programme. (Ceci est communément appelé la troisième pile, bien que Mini-Flak n'ait pas de deuxième pile, donc "pile de travail" est probablement un meilleur nom dans le contexte Mini-Flak.) Les autres possibilités de stockage de données seraient la pile principale / première (qui ne fonctionne pas parce que c'est là que notre sortie doit aller, et nous ne pouvons pas déplacer la sortie au-delà du stockage d'une manière efficace à distance), et codée en un bignum dans un seul élément de pile (ce qui ne convient pas à ce problème car cela prend du temps exponentiel pour en extraire des données); lorsque vous les supprimez, la pile de travail est le seul emplacement restant.
Afin de "stocker" les données sur cette pile, nous utilisons des commandes asymétriques (dans ce cas, la première moitié d'une (…)
commande), qui seront équilibrées dans le formateur de chaîne de données plus tard. Chaque fois que nous fermons l'une de ces commandes dans le formateur, elle va pousser la somme d'une donnée tirée de la chaîne de données et les valeurs de retour de toutes les commandes à ce niveau d'imbrication dans le formateur; nous pouvons nous assurer que ces derniers s'ajoutent à zéro, de sorte que le formateur ne voit que des valeurs uniques prises dans la chaîne de données.
Le format est très simple:, (
suivi de n copies de ()
, où n est le nombre que nous voulons stocker. (Notez que cela signifie que nous ne pouvons stocker que des nombres non négatifs et que le dernier élément de la chaîne de données doit être positif.)
Un point légèrement peu intuitif de la chaîne de données est l'ordre dans lequel elle se trouve. Le "début" de la chaîne de données est la fin la plus proche du début du programme, c'est-à-dire le niveau d'imbrication le plus à l'extérieur; cette partie est formatée en dernier (lorsque le formateur s'étend des niveaux d'imbrication les plus internes aux plus externes). Cependant, bien qu'il soit formaté en dernier, il est imprimé en premier, car les valeurs insérées en premier dans la pile sont imprimées en dernier par l'interpréteur Mini-Flak. Le même principe s'applique au programme dans son ensemble; nous devons d'abord formater l'outro, puis le formateur de chaîne de données, puis la chaîne de données, puis l'intro, c'est-à-dire l'inverse de l'ordre dans lequel ils sont stockés dans le programme.
Le formateur de chaîne de données
)[({})(
([({})]({}{}))
{
((()[()]))
}{}
{
{
({}(((({}())[()])))[{}()])
}{}
(({}))
((()[()]))
}{}
)]{}
Le formateur de chaîne de données est constitué de 241 sections qui ont chacune un code identique (une section a un commentaire légèrement différent), chacune formatant un caractère spécifique de la chaîne de données. (Nous ne pouvions pas utiliser une boucle ici: nous avons besoin d'un asymétrique )
pour lire la chaîne de données en faisant correspondre son asymétrique (
, et nous ne pouvons pas en mettre une dans la {…}
boucle, la seule forme de boucle qui existe. Donc, à la place, nous " dérouler "le formateur et obtenir simplement l'intro / sortie pour sortir la chaîne de données avec le décalage du formateur 241 fois.)
)[({})( … )]{}
La partie la plus externe d'un élément de mise en forme lit un élément de la chaîne de données; la simplicité de l'encodage de la chaîne de données conduit à un peu de complexité dans sa lecture. Nous commençons par fermer l'inégalé (…)
dans la chaîne de données, puis annulons ( […]
) deux valeurs: la donnée que nous venons de lire dans la chaîne de données ( ({})
) et la valeur de retour du reste du programme. Nous copions la valeur de retour du reste de l'élément de formateur avec (…)
et ajoutons la copie à la version négative avec {}
. Le résultat final est que la valeur de retour de l'élément de chaîne de données et de l'élément de formateur ensemble est la donnée moins la donnée moins la valeur de retour plus la valeur de retour, ou 0; cela est nécessaire pour que l'élément de chaîne de données suivant produise la valeur correcte.
([({})]({}{}))
Le formateur utilise l'élément de pile supérieur pour savoir dans quel mode il se trouve (0 = format dans le formatage de la chaîne de données, toute autre valeur = l'offset avec lequel sortir). Cependant, juste après avoir lu la chaîne de données, la donnée est au-dessus du format sur la pile, et nous voulons les inverser. Ce code est une variante plus courte du code d'échange Brain-Flak, prenant a au - dessus de b à b au - dessus de a + b ; non seulement il est plus court, il est aussi (dans ce cas spécifique) plus utile, car l'effet secondaire de l'ajout de b à a n'est pas problématique lorsque b est 0, et lorsque b n'est pas 0, il fait le calcul du décalage pour nous.
{
((()[()]))
}{}
{
…
((()[()]))
}{}
Brain-Flak n'a qu'une seule structure de flux de contrôle, donc si nous voulons autre chose qu'une while
boucle, cela prendra un peu de travail. Il s'agit d'une structure "négative"; s'il y a un 0 au-dessus de la pile, il le supprime, sinon il place un 0 au-dessus de la pile. (Cela fonctionne assez simplement: tant qu'il n'y a pas de 0 au-dessus de la pile, poussez 1 - 1 à la pile deux fois; lorsque vous avez terminé, éclatez l'élément supérieur de la pile.)
Il est possible de placer du code à l'intérieur d'une structure de négation, comme on le voit ici. Le code ne s'exécutera que si le haut de la pile était différent de zéro; donc si nous avons deux structures négatives, en supposant que les deux premiers éléments de la pile ne sont pas tous les deux à zéro, ils s'annuleront l'un l'autre, mais tout code à l'intérieur de la première structure ne s'exécutera que si l'élément supérieur de la pile n'était pas nul et le code à l'intérieur la deuxième structure ne fonctionnera que si l'élément de pile supérieur était nul. En d'autres termes, c'est l'équivalent d'une instruction if-then-else.
Dans la clause "then", qui s'exécute si le format est différent de zéro, nous n'avons en fait rien à faire; ce que nous voulons, c'est pousser les données + offset vers la pile principale (afin qu'elles puissent être sorties à la fin du programme), mais elles sont déjà là. Il nous suffit donc de traiter le cas de l'encodage de l'élément de chaîne de données sous forme source.
{
({}(((({}())[()])))[{}()])
}{}
(({}))
Voici comment nous procédons. La {({}( … )[{}()])}{}
structure doit être familière comme une boucle avec un nombre spécifique d'itérations (ce qui fonctionne en déplaçant le compteur de boucle vers la pile de travail et en le maintenant là-bas; il sera à l'abri de tout autre code, car l'accès à la pile de travail est lié à le niveau d'imbrication du programme). Le corps de la boucle est ((({}())[()]))
, ce qui fait trois copies de l'élément de pile supérieur et ajoute 1 au plus bas. En d'autres termes, il transforme un 40 au-dessus de la pile en 40 au-dessus de 40 au-dessus de 41, ou considéré comme ASCII, (
en (()
; l' exécution de ce répétitivement faire (
en (()
en (()()
en (()()()
et ainsi de suite, et est donc un moyen simple de générer notre chaîne de données ( en supposant qu'il ya un (
au - dessus de la pile déjà).
Une fois que nous avons terminé la boucle, (({}))
duplique le haut de la pile (de sorte qu'elle commence maintenant ((()…
plutôt que (()…
. Le début (
sera utilisé par la prochaine copie du formateur de chaîne de données pour formater le caractère suivant (il le développera en (()(()…
puis (()()(()…
, et ainsi de suite, donc cela génère la séparation (
dans la chaîne de données).
%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'
Il y a un dernier peu d'intérêt dans le formateur de chaînes de données. OK, donc c'est surtout l'outro décalé de 4 points de code vers le bas; cependant, cette apostrophe à la fin peut sembler déplacée. '
(point de code 39) se transformerait en +
(point de code 43), qui n'est pas une commande Brain-Flak, donc vous avez peut-être deviné qu'il est là dans un autre but.
La raison en est que le formateur de chaînes de données s'attend à ce qu'il y ait déjà un (
sur la pile (il ne contient aucun littéral 40 nulle part). le'
est en fait au début du bloc qui est répété pour constituer le formateur de chaîne de données, pas à la fin, donc après que les caractères du formateur de chaîne de données ont été poussés sur la pile (et que le code est sur le point de passer à l'impression de la chaîne de données lui-même), l'outro ajuste le 39 au sommet de la pile en un 40, prêt à être utilisé par le formateur (le formateur en cours d'exécution cette fois, pas sa représentation dans la source). C'est pourquoi nous avons "presque 241" copies du formateur; il manque le premier caractère de la première copie. Et ce caractère, l'apostrophe, est l'un des trois seuls caractères de la chaîne de données qui ne correspondent pas au code Mini-Flak quelque part dans le programme; c'est purement comme méthode pour fournir une constante.
L'intro et l'outro
(((()()()()){}))
{{}
(({})[(()()()())])
(({})(
{{}{}((()[()]))}{}
(((((((({})){}){}{})){}{}){}){}())
{
({}(
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
…
)[{}()])
}{}
{}({}())
)[{}])
(({})(()()()()){})
}{}{}␊
L'intro et l'outro sont conceptuellement la même partie du programme; la seule raison pour laquelle nous faisons une distinction est que l'outro doit être sortie avant la chaîne de données et son formateur (pour qu'elle s'imprime après eux), tandis que l'intro doit être sortie après eux (impression avant eux).
(((()()()()){}))
Nous commençons par placer deux copies de 8 sur la pile. Il s'agit du décalage pour la première itération. La deuxième copie est parce que la boucle principale s'attend à ce qu'il y ait un élément indésirable au-dessus de la pile au-dessus du décalage, laissé derrière le test qui décide s'il doit exister la boucle principale, et nous devons donc y placer un élément indésirable afin que il ne jette pas l'élément que nous voulons réellement; une copie est la façon la plus simple (donc la plus rapide de produire) de le faire.
Il y a d'autres représentations du nombre 8 qui ne sont pas plus longues que celle-ci. Cependant, lorsque vous optez pour le code le plus rapide, c'est certainement la meilleure option. D'une part, l'utilisation ()()()()
est plus rapide que, disons, (()()){}
parce que, bien que les deux aient 8 caractères, le premier est un cycle plus rapide, car il (…)
est compté comme 2 cycles, mais ()
seulement comme un. Cependant, l'enregistrement d'un cycle est négligeable par rapport à une considération beaucoup plus importante pour un quine : (
et )
a des points de code beaucoup plus faibles que {
et }
, donc la génération du fragment de données pour eux sera beaucoup plus rapide (et le fragment de données prendra moins de place dans le code, aussi).
{{} … }{}{}
La boucle principale. Cela ne compte pas les itérations (c'est une while
boucle, pas une for
boucle, et utilise un test pour sortir). Une fois qu'il se termine, nous jetons les deux premiers éléments de la pile; l'élément supérieur est un 0 inoffensif, mais l'élément ci-dessous sera le "format à utiliser à la prochaine itération", qui (étant un décalage négatif) est un nombre négatif, et s'il y a des nombres négatifs sur la pile lorsque le Mini -Le programme Flak se ferme, l'interprète se bloque en essayant de les sortir.
Parce que cette boucle utilise un test explicite pour éclater, le résultat de ce test sera laissé sur la pile, nous le rejetons donc comme première chose que nous faisons (sa valeur n'est pas utile).
(({})[(()()()())])
Ce code pousse 4 et f - 4 au-dessus d'un élément de pile f , tout en laissant cet élément en place. Nous calculons à l'avance le format de la prochaine itération (alors que nous avons la constante 4 à portée de main), et obtenons simultanément la pile dans le bon ordre pour les prochaines parties du programme: nous utiliserons f comme format pour cette itération, et le 4 est nécessaire avant cela.
(({})( … )[{}])
Cela enregistre une copie de f - 4 sur la pile de travail, afin que nous puissions l'utiliser pour la prochaine itération. (La valeur de f sera toujours présente à ce moment-là, mais ce sera à un endroit gênant de la pile, et même si nous pouvions le manœuvrer au bon endroit, nous devions passer des cycles à en soustraire 4, et imprime le code pour effectuer cette soustraction. Il est beaucoup plus facile de le stocker maintenant.)
{{}{}((()[()]))}{}
Un test pour voir si le décalage est 4 (c'est-à-dire que f - 4 est 0). Si c'est le cas, nous imprimons le formateur de chaîne de données, nous devons donc exécuter la chaîne de données et son formateur 241 fois plutôt qu'une seule fois à ce décalage. Le code est assez simple: si f -4 est différent de zéro, remplacez le f -4 et le 4 lui-même par une paire de zéros; puis dans les deux cas, éclatez l'élément supérieur de la pile. Nous avons maintenant un nombre au-dessus de f sur la pile, soit 4 (si nous voulons imprimer cette itération 241 fois) ou 0 (si nous voulons l'imprimer une seule fois).
(
((((((({})){}){}{})){}{}){}){}
()
)
C'est une sorte intéressante de constante Brain-Flak / Mini-Flak; la longue ligne ici représente le nombre 60. Vous pouvez être confus à l'absence de ()
, qui sont normalement partout dans les constantes Brain-Flak; ce n'est pas un nombre régulier, mais un chiffre de l'Église, qui interprète les nombres comme une opération de duplication. Par exemple, le chiffre de l'Église pour 60, vu ici, fait 60 copies de son entrée et les combine tous ensemble en une seule valeur; dans Brain-Flak, les seules choses que nous pouvons combiner sont des nombres réguliers, par addition, donc nous finissons par ajouter 60 copies du haut de la pile et ainsi multiplier le haut de la pile par 60.
En guise de remarque, vous pouvez également utiliser un moteur de recherche de chiffres de sous-charge, qui génère des chiffres d'église dans la syntaxe de sous-charge, pour trouver également le numéro approprié dans Mini-Flak. Les chiffres de sous-charge (autres que zéro) utilisent les opérations "dupliquer l'élément de pile supérieur" :
et "combiner les deux éléments de pile supérieurs" *
; Ces deux opérations existent dans Brain-Flak, de sorte que vous traduisez juste :
à )
, *
à {}
, précédez un {}
, et ajouter suffisamment (
au début à l' équilibre (ce qui utilise un mélange bizarre de la pile principale et la pile de travail, mais il fonctionne).
Ce fragment de code particulier utilise le numéro d'église 60 (en fait un extrait "multiplier par 60"), avec un incrément, pour générer l'expression 60 x + 1. Donc, si nous avions un 4 à l'étape précédente, cela nous donne une valeur de 241, ou si nous avions un 0, nous obtenons simplement une valeur de 1, c'est-à-dire que cela calcule correctement le nombre d'itérations dont nous avons besoin.
Le choix de 241 n'est pas un hasard; c'était une valeur choisie pour être a) approximativement la durée à laquelle le programme se terminerait de toute façon et b) 1 plus de 4 fois un nombre rond. Les nombres ronds, 60 dans ce cas, ont tendance à avoir des représentations plus courtes en tant que chiffres de l'Église parce que vous avez plus de flexibilité dans les facteurs à copier. Le programme contient un rembourrage plus tard pour porter la longueur à 241 exactement.
{
({}(
…
)[{}()])
}{}
Il s'agit d'une boucle for, comme celle vue précédemment, qui exécute simplement le code à l'intérieur un certain nombre de fois égal au sommet de la pile principale (qu'il consomme; le compteur de boucle lui-même est stocké sur la pile de travail, mais la visibilité de qui est lié au niveau d'imbrication du programme et il est donc impossible pour autre chose que la boucle for elle-même d'interagir avec lui). Cela exécute en fait la chaîne de données et son formateur 1 ou 241 fois, et comme nous avons maintenant ajouté toutes les valeurs que nous utilisions pour notre calcul de flux de contrôle à partir de la pile principale, nous avons le format à utiliser en plus, prêt pour le formateur à utiliser.
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
Le commentaire ici n'est pas entièrement sans intérêt. D'une part, il existe quelques commandes Brain-Flak; le )
à la fin est naturellement généré comme un effet secondaire de la façon dont les transitions entre les différents segments du programme fonctionnent, donc (
au début a été ajouté manuellement pour l'équilibrer (et malgré la longueur du commentaire à l'intérieur, mettre un commentaire à l'intérieur une ()
commande est toujours une ()
commande, donc tout ce qu'elle fait est d'ajouter 1 à la valeur de retour de la chaîne de données et de son formateur, quelque chose que la boucle for ignore complètement).
Plus particulièrement, ces caractères NUL au début du commentaire ne sont clairement pas compensés par quoi que ce soit (même la différence entre +8 et -4 n'est pas suffisante pour transformer un (
en NUL). Il s'agit d'un pur rembourrage pour porter la chaîne de données de 239 éléments à 241 éléments (qui se paient facilement d'eux-mêmes: il faudrait beaucoup plus de deux octets pour générer 1 contre 239 plutôt que 1 contre 241 lors du calcul du nombre d'itérations nécessaires ). NUL a été utilisé comme caractère de remplissage car il a le point de code le plus bas possible (ce qui rend le code source de la chaîne de données plus court et donc plus rapide à produire).
{}({}())
Déposez l'élément de pile supérieur (le format que nous utilisons), ajoutez 1 au suivant (le dernier caractère à sortir, c'est-à-dire le premier caractère à imprimer, de la section de programme que nous venons de formater). Nous n'avons plus besoin de l'ancien format (le nouveau format se cache sur la pile de travail); et l'incrément est inoffensif dans la plupart des cas, et change le '
à une extrémité de la représentation source du formateur de chaîne de données en un (
(qui est requis sur la pile pour la prochaine fois que nous exécuterons le formateur, pour formater la chaîne de données elle-même). Nous avons besoin d'une transformation comme celle-ci dans l'outro ou l'intro, car forcer le début de chaque élément de formateur de chaîne de données le (
rendrait un peu plus complexe (car nous aurions besoin de fermer (
puis d'annuler son effet plus tard), etnous aurions en quelque sorte besoin de générer un extra (
quelque part car nous n'avons que près de 241 copies du formateur, pas toutes les 241 (il est donc préférable qu'un personnage inoffensif comme '
celui qui manque).
(({})(()()()()){})
Enfin, le test de sortie de boucle. Le sommet actuel de la pile principale est le format dont nous avons besoin pour la prochaine itération (qui vient de revenir de la pile de travail). Cela le copie et ajoute 8 à la copie; la valeur résultante sera ignorée la prochaine fois dans la boucle. Cependant, si nous venons d'imprimer l'intro, le décalage était de -4 donc le décalage pour la "prochaine itération" sera de -8; -8 + 8 est 0, donc la boucle sortira plutôt que de continuer sur l'itération par la suite.