Conseils pour écrire des quines


30

Une est un programme qui produit une sortie identique au code source du programme. Sur ce site Web, nous ne nous soucions généralement que des quines appropriés (au moment de la rédaction, la définition actuelle est "une partie de la sortie est codée par une autre partie du programme").

Quels conseils avez-vous pour écrire des quines appropriées ou des programmes avec des propriétés de quine? Comme d'habitude, chaque conseil doit être dans une réponse différente.

tips  quine 

Réponses:


14

Utilisez evalpour réduire le besoin de copier le code

La majorité des quines nécessitent deux copies du code; un à exécuter, un sous forme de données. Cela peut finir par doubler la longueur du code source, le rendre plus difficile à maintenir et aggraver le score si vous écrivez le quine pour une compétition de .

La fusion des deux copies signifie qu'une seule information doit être utilisée à deux fins. Essayer de traiter votre code comme des données n'est souvent pas possible et est généralement considéré comme de la triche quand il l'est. Cependant, le traitement des données en tant que code peut être effectué dans de nombreuses langues via l'utilisation d'une fonction intégrée, généralement appelée eval. En tant que tel, votre quine consiste essentiellement à stocker le corps principal de votre quine dans une variable (afin que vous puissiez vous y référer plusieurs fois), puis à évaluer cette variable.

Voici un exemple de la façon dont cela fonctionne (l'exemple est écrit en Python, mais quelque chose de similaire fonctionne dans de nombreuses autres langues):

d='print("d="+repr(d)+"\\neval(d)")'
eval(d)

2
@QPaysTaxes: Mon objectif est de rendre mon code ici aussi lisible et maintenable que le permet la condition de victoire. Malheureusement, il est toujours impossible de distinguer le bruit de ligne ASCII (ou tout simplement le bruit de ligne normal si j'utilise Jelly) de personnes qui ne sont pas habituées à la langue.

14

Profitez du formatage des chaînes

L'une des façons les plus simples de créer une quine consiste à définir une chaîne, puis à mettre la chaîne en elle-même avec un formatage de chaîne.

s='s=%r;print s%%s';print s%s

Donc, dans cet exemple Python quine, nous déclarons une chaîne avec la première partie égale à ce qui est avant la chaîne s=, puis nous permettons à la chaîne d'être insérée avec le formatage avec %r, et enfin nous mettons ce qui vient après la chaîne pour l'imprimer et la formater . La nouvelle ligne de fin est car printimprime une nouvelle ligne de fin.

Donc le modèle est vraiment celui-ci, en Python:

<A>'<A>%r<B>'<B>

Pour développer le quine existant avec plus de code:

<more A>s='<more A>s=%r;print s%%s<more B>';print s%s<more B>

9

Fonctions stringifiées

Dans plusieurs langages, les objets fonction (ou constructions équivalentes) stockent implicitement leur code source et le renverront lors de leur conversion en chaîne. Cela permet des quines compactes sans utiliser d' évaluation de chaîne . Un exemple notable d'un tel langage est JavaScript:

function f(){console.log(f+"f()")}f()

Ce code définit et appelle une fonction fqui, lorsqu'elle est appelée, imprime son propre code source suivi d'un appel à lui-même. La seule partie du programme qui doit être dupliquée est l'appel de fonction f(). Bien entendu, le corps de la fonction peut inclure une "charge utile" arbitraire de code qui sera également exécutée lors de l'appel de la fonction.


Une version plus compacte de la même astuce fonctionne dans les langages de golf GolfScript :

{".~"}.~

et CJam :

{"_~"}_~

Chacune de ces quines définit d'abord un bloc de code anonyme (entouré d'accolades), qui se comporte un peu comme un objet fonction en JavaScript: il peut être exécuté, et s'il est stratifié, il retourne son code source. Le reste du code ( .~en GolfScript ou _~en CJam) exécute ensuite le bloc, tout en en laissant une copie sur la pile. Le code à l'intérieur du bloc pousse ensuite une chaîne sur la pile qui répète le code à l'extérieur du bloc. Lorsque l'interpréteur se ferme, il stringifie et imprime automatiquement tout ce qui reste sur la pile. Comme avec l'exemple JavaScript, ces blocs de code peuvent également être conçus pour transporter et exécuter une charge utile arbitraire de code supplémentaire sans avoir à le dupliquer.


9

Utilisez des délimiteurs de chaînes qui s'emboîtent sans s'échapper

Souvent, l'une des parties les plus difficiles de l'écriture d'une quine est l'étape d'échappement. Cela est nécessaire dans presque tous les quines; le problème est que vous stockez des données d'une manière ou d'une autre, et vous devez répliquer le code qui stocke les données dans la sortie du quine. Ce code contiendra une forme d'échappement des données, donc le programme verra une forme non échappée, et vous devrez la rééchapper.

La façon la plus simple de gérer l'étape de non-échappement est si les formes échappées et non échappées des données diffèrent uniquement en présence ou en l'absence de délimiteurs de chaîne. L'échappement est donc une simple question d'ajouter une nouvelle paire de délimiteurs de chaîne autour de la chaîne. Malheureusement, cela ne peut clairement fonctionner que si les délimiteurs de chaîne eux-mêmes peuvent être exprimés dans les données sans s'échapper.

Perl est un bon exemple de langage où cette astuce fonctionne. Bien que ses délimiteurs de chaîne habituels soient "…"ou '…', les q(…)nids les moins couramment utilisés , permettant d'écrire ce type de quine:

$_=q($_=q(0);s/\d/$_/;print);s/\d/$_/;print

Il s'agit d'un code + quine de données. s///est une opération de remplacement de chaîne d'expression régulière; nous utilisons 0comme marqueur, et nous le faisons correspondre dans l'expression régulière en tant que \d("n'importe quel chiffre"), pour éviter d'utiliser le marqueur plus d'une fois (bien que comme une autre optimisation, nous aurions pu en fait simplement l'utiliser à 0nouveau, car Perl s///ne remplace que la première occurrence par défaut). Notez qu'aucune étape d'échappement explicite n'est nécessaire ici, car les q(…)délimiteurs peuvent simplement être inclus littéralement dans la chaîne de données.


8

Code + quines de données

La structure la plus générale d'un quine ressemble à ce pseudocode:

data = " une version échappée de tout le programme,
        avec cette chaîne remplacée par un marqueur "
programme = data.replace (
  une expression qui évalue le marqueur mais ne le mentionne pas ,
  échappé (données))
programme d'impression;

Cette structure peut être utilisée pour écrire un quine (assez naïf) dans la plupart des langues. Cependant, il a tendance à marquer assez mal sur la plupart des systèmes de notation, car vous devez écrire l'intégralité du programme deux fois. Cependant, la plupart des structures quine peuvent être considérées comme des optimisations de celle-ci.

Il y a quelques subtilités à cela. Dans certaines langues, la partie la plus difficile de cette opération consiste à écrire le code d'échappement; dans de nombreuses langues, produire le marqueur sans mentionner son nom est difficile; et dans certaines langues ésotériques, vous devrez inventer votre propre type de chaîne littérale. Les trois opérations n'ont cependant pas tendance à causer trop de problèmes.

Par exemple, nous pouvons écrire un quine Python échappant une chaîne en utilisant repret en utilisant la x"chaîne de séquence de 2 caractères (qui est représentable comme "x\"", c'est-à-dire n'utilisant pas la séquence x"dans la représentation de chaîne de la chaîne elle-même) comme marqueur:

d='d=x"\nprint(str.replace(d,"x\\"",repr(d)))'
print(str.replace(d,"x\"",repr(d)))

2
Il peut être intéressant de noter (peut-être dans une autre réponse) que l'insertion de la chaîne à la position d'un marqueur est souvent coûteuse dans les esolangs, et il pourrait être utile de structurer le code de telle sorte que la chaîne elle-même soit la première ou la dernière chose (peut-être séparée de la fin par un ou deux caractères que vous pouvez coder en dur) afin que vous sachiez où il doit aller.
Martin Ender

@MartinEnder: Je suis d'accord que cela mérite d'être mentionné, mais c'est probablement une autre réponse (plutôt qu'un commentaire ou une modification dans cette réponse). La plupart des astuces de quine sont des modifications de cette structure générale, donc je voulais d'abord la diffuser comme une astuce, car beaucoup de gens ne savent pas par où commencer pour écrire une quine.

Une alternative à un marqueur est d'utiliser deux cordes, je l'ai fait pour Glass .
Ørjan Johansen

4

Exploiter le code source d'encapsulation

Dans un certain nombre de langues (principalement des langues 2D), le code source peut s'enrouler; dans certaines circonstances (par exemple dans Befunge-98, si votre programme est à une ligne), le fait de dépasser la fin du programme vous ramènera au début du programme. Ce type de comportement non linéaire signifie que vous pouvez écrire du code à l'intérieur et à l'extérieur d'un littéral de chaîne en même temps; un inégalé "(ou quel que soit le délimiteur de chaîne) vous donnera effectivement une chaîne contenant tout le reste du programme (à l'exception de "lui - même).

Un problème avec l'utilisation de cette astuce est que vous obtiendrez la chaîne telle que vue du point de vue de la ", plutôt que du début du programme (comme vous le souhaitez). En tant que tel, il est probablement plus facile de réorganiser le programme de sorte que le "s'affiche au début ou à la fin. Cela signifie souvent couper votre programme en plusieurs morceaux et utiliser toutes les commandes de contrôle de flux intéressantes / inhabituelles de votre langue (la plupart des langues qui permettent aux littéraux de chaîne d'enrouler le programme en ont une bonne sélection).

Un bon exemple est le quine @ Justin's Befunge-98 :

<@,+1!',k9"

L'incomparable "à la fin du programme enveloppe tout le programme dans un littéral de chaîne, donc (courir de droite à gauche en raison du <au début) tout ce que nous avons à faire est de sortir le programme ( 9k), puis de sortir le double guillemet ( '!1+,) et exit ( @). Cela évite d'avoir besoin de deux copies du programme (une sous forme de code, une sous forme de données); les deux copies sont le même morceau de code, juste interprété de différentes manières.


4

Rappelez-vous la structure d'un quine

J'aime penser aux quines en trois parties, plutôt qu'en 2:

  • Partie 1: Générez une représentation des données des parties 2 et 3.
  • Partie 2: utilisez les données pour imprimer de façon algorithmique la partie 1.
  • Partie 3: décodez la représentation pour imprimer les parties 2 et 3.

Cela peut faciliter la réflexion sur les quines. Voici un quine Python, chaque ligne correspondant à une partie:

s = "print(\"s = \" + repr(s))\nprint(s)"
print("s = " + repr(s))
print(s)

Parfois, vous utilisez un evalou similaire pour supprimer la duplication, mais j'ai généralement constaté que cela aide à écrire des quines simples.

Jetons un coup d'œil à deux quines de sous-charge différentes. C'est le premier:

(:aSS):aSS

La première partie est (:aSS), qui génère la représentation des données. Le second est :aS, qui imprime (:aSS). La troisième partie est S, qui imprime :aSS.

Voici le deuxième quine:

(:aS(:^)S):^

Au début, cela ne semble pas correspondre. Mais si vous développez le quine, vous obtenez:

(:aS(:^)S):aS(:^)S

Maintenant, (:aS(:^)S)c'est la partie 1, la :aSpartie 2 et la (:^)Spartie 3.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.