Programmation introspective: code qui analyse sa source et sa sortie


13

Écrivez un programme qui produit le nombre total de caractères et la fréquence de chaque caractère dans sa source et sa sortie. Vous devez suivre le format illustré dans l'exemple.

Exemple

Si votre code était

abb1

Sa sortie devrait être

My source has 4 characters.
1 is "a"
2 are "b"
1 is "1"
Besides unquoted numbers, my output has 383 characters.
34 are "
"
79 are " "
63 are """
2 are "'"
2 are ","
4 are "."
2 are "1"
2 are "B"
2 are "I"
2 are "M"
39 are "a"
4 are "b"
6 are "c"
4 are "d"
38 are "e"
3 are "g"
5 are "h"
4 are "i"
4 are "m"
3 are "n"
8 are "o"
3 are "p"
2 are "q"
38 are "r"
12 are "s"
8 are "t"
7 are "u"
3 are "y"
It's good to be a program.

(La sortie doit aller à stdout.)

Notez, par exemple, que la sortie contient deux m en majuscule. Un pour Myet un pour 2 are "M". Cela doit être vrai pour tous les caractères afin que la sortie ne se contredit en aucune façon.

Les nombres non cotés sont ignorés dans la sortie pour éviter des ensembles de fréquences insatisfaisants. Par exemple, 1 is "1"est incorrect si les deux 1 sont comptés. Il devrait être lu 2 are "1", mais il n'y a encore qu'un seul 1.

Clarifications de format

  • "est" doit être utilisé pour les occurrences d'un seul caractère.

  • "are" doit être utilisé pour les occurrences de plusieurs caractères.

  • "is" ne devrait jamais apparaître dans la liste des caractères de sortie car il serait superflu. 1 is 'Z'fait référence au Z en lui-même, de sorte que toute la ligne peut être supprimée.

  • Les trois phrases complètes doivent apparaître dans l'ordre avec les listes de fréquence des caractères entre les deux (comme le montre l'exemple). Ainsi, votre sortie commencera par My source...et se terminera par ...be a program.. Notez qu'il n'y a pas de nouvelle ligne à la fin de la sortie.

  • Les listes de fréquences des caractères elles-mêmes peuvent être dans n'importe quel ordre.

  • Les sauts de ligne comptent comme un caractère (au cas où ils seraient \ r \ n).

Vérificateur de format

Le script Python suivant prend votre code et sa sortie sous forme de chaînes et affirme que la sortie n'a pas de contradictions. Il fournit un message d'erreur utile en cas de problème. Vous pouvez l'exécuter en ligne sur http://ideone.com/6H0ldu en le forçant, en remplaçant les chaînes CODE et OUTPUT, puis en l'exécutant. Il ne donnera jamais de faux positifs ou négatifs (en supposant qu'il est exempt d'erreur).

#Change the CODE and OUTPUT strings to test your program

CODE = r'''abb1'''

OUTPUT = r'''My source has 4 characters.
1 is "a"
2 are "b"
1 is "1"
Besides unquoted numbers, my output has 383 characters.
34 are "
"
79 are " "
63 are """
2 are "'"
2 are ","
4 are "."
2 are "1"
2 are "B"
2 are "I"
2 are "M"
39 are "a"
4 are "b"
6 are "c"
4 are "d"
38 are "e"
3 are "g"
5 are "h"
4 are "i"
4 are "m"
3 are "n"
8 are "o"
3 are "p"
2 are "q"
38 are "r"
12 are "s"
8 are "t"
7 are "u"
3 are "y"
It's good to be a program.'''

#######################################################

import re

amountPattern = r'(\d+) (is|are) "(.)"\n'

class IntrospectionException(Exception):
    pass

def getClaimedAmounts(string, errorOnIs):
    groups = re.findall(amountPattern, string, re.DOTALL)

    for amount, verb, char in groups:
        if verb == 'is':
            if errorOnIs:
                raise IntrospectionException('\'1 is "%s"\' is unnecessary' % char)
            elif amount != '1':
                raise IntrospectionException('At "%s", %s must use "are"' % (char, amount))
        elif verb == 'are' and amount == '1':
            raise IntrospectionException('At "%s", 1 must use "is"' % char)

    amounts = {}
    for amount, verb, char in groups:
        if char in amounts:
            raise IntrospectionException('Duplicate "%s" found' % char)
        amounts[char] = int(amount)
    return amounts

def getActualAmounts(string):
    amounts = {}
    for char in string:
        if char in amounts:
            amounts[char] += 1
        else:
            amounts[char] = 1
    return amounts

def compareAmounts(claimed, actual):
    for char in actual:
        if char not in claimed:
            raise IntrospectionException('The amounts list is missing "%s"' % char)
    for char in actual: #loop separately so missing character errors are all found first
        if claimed[char] != actual[char]:
            raise IntrospectionException('The amount of "%s" characters is %d, not %d' % (char, actual[char], claimed[char]))
    if claimed != actual:
        raise IntrospectionException('The amounts are somehow incorrect')

def isCorrect(code, output):
    p1 = r'^My source has (\d+) characters\.\n'
    p2 = r'Besides unquoted numbers, my output has (\d+) characters\.\n'
    p3 = r"It's good to be a program\.$"
    p4 = '%s(%s)*%s(%s)*%s' % (p1, amountPattern, p2, amountPattern, p3)

    for p in [p1, p2, p3, p4]:
        if re.search(p, output, re.DOTALL) == None:
            raise IntrospectionException('Did not match the regex "%s"' % p)

    claimedCodeSize = int(re.search(p1, output).groups()[0])
    actualCodeSize = len(code)
    if claimedCodeSize != actualCodeSize:
        raise IntrospectionException('The code length is %d, not %d' % (actualCodeSize, claimedCodeSize))

    filteredOutput = re.sub(r'([^"])\d+([^"])', r'\1\2', output)

    claimedOutputSize = int(re.search(p2, output).groups()[0])
    actualOutputSize = len(filteredOutput)
    if claimedOutputSize != actualOutputSize:
        raise IntrospectionException('The output length (excluding unquoted numbers) is %d, not %d' % (actualOutputSize, claimedOutputSize))

    splitIndex = re.search(p2, output).start()

    claimedCodeAmounts = getClaimedAmounts(output[:splitIndex], False)
    actualCodeAmounts = getActualAmounts(code)
    compareAmounts(claimedCodeAmounts, actualCodeAmounts)

    claimedOutputAmounts = getClaimedAmounts(output[splitIndex:], True)
    actualOutputAmounts = getActualAmounts(filteredOutput)
    compareAmounts(claimedOutputAmounts, actualOutputAmounts)

def checkCorrectness():
    try:
        isCorrect(CODE, OUTPUT)
        print 'Everything is correct!'
    except IntrospectionException as e:
        print 'Failed: %s.' % e

checkCorrectness()

Notation

C'est du code-golf. La soumission avec le moins de personnages gagne. Les soumissions doivent passer le vérificateur de format pour être valides. Les failles standard s'appliquent, bien que vous puissiez lire votre propre code source et / ou coder en dur votre sortie .


La lecture de votre propre fichier source est-elle autorisée?
Ventero

@MrLore Il peut y avoir d'autres erreurs mais je viens de réaliser que les guillemets triples ('' ') échappent toujours aux choses avec barre oblique inverse. Cela peut être lié à votre problème. Je le répare maintenant.
Calvin's Hobbies

@Ventero Certainement!
Calvin's Hobbies

@MrLore Les regexps autorisent certains faux positifs, oui. Pour résoudre le problème des barres obliques inverses dans les guillemets triples, utilisez des chaînes brutes ( r'''CODE''').
Ventero

1
@MrLore Correction de points non échappés. Merci d'avoir remarqué!
Calvin's Hobbies

Réponses:


2

CJam - 189

{`"_~"+:T;"Besides unquoted numbers, my output has &It's good to be a program.&My source has & characters.
"'&/~_]:X2=T,X3=3i({T_&:B{TI/,(" are ":AM`I*N}fIXK=]o
XBA`N+f+2*+s:T,X3=}fK'q];}_~

Essayez-le sur http://cjam.aditsu.net/

Production:

My source has 189 characters.
3 are "{"
3 are "`"
6 are """
4 are "_"
3 are "~"
4 are "+"
5 are ":"
5 are "T"
2 are ";"
3 are "B"
8 are "e"
9 are "s"
2 are "i"
3 are "d"
17 are " "
6 are "u"
2 are "n"
2 are "q"
8 are "o"
6 are "t"
3 are "m"
2 are "b"
7 are "r"
4 are ","
2 are "y"
2 are "p"
3 are "h"
7 are "a"
5 are "&"
4 are "I"
3 are "'"
2 are "g"
2 are "."
2 are "M"
3 are "c"
2 are "
"
2 are "/"
3 are "]"
5 are "X"
2 are "2"
4 are "="
3 are "3"
2 are "("
2 are "A"
2 are "*"
2 are "N"
3 are "}"
3 are "f"
2 are "K"
Besides unquoted numbers, my output has 988 characters.
3 are "B"
108 are "e"
11 are "s"
3 are "i"
5 are "d"
214 are " "
8 are "u"
4 are "n"
3 are "q"
9 are "o"
9 are "t"
5 are "m"
4 are "b"
108 are "r"
3 are ","
4 are "y"
4 are "p"
6 are "h"
108 are "a"
3 are "I"
3 are "'"
4 are "g"
5 are "."
3 are "M"
7 are "c"
102 are "
"
2 are "{"
198 are """
2 are "`"
2 are "_"
2 are "~"
2 are "+"
2 are ":"
2 are "T"
2 are ";"
2 are "&"
2 are "/"
2 are "]"
2 are "X"
2 are "2"
2 are "="
2 are "3"
2 are "("
2 are "A"
2 are "*"
2 are "N"
2 are "}"
2 are "f"
2 are "K"
It's good to be a program.

11

Ruby, 269 (311, 367) caractères

J'ai trois solutions différentes pour ce défi. Chacun d'eux utilise un ensemble différent d'astuces:

Solution "correcte", 367 caractères:

La solution la plus longue est plus ou moins juste une preuve de concept qu'il est possible de résoudre ce défi sans aucune astuce - et n'est pas presque entièrement jouée. C'est un vrai quine (c'est-à-dire qu'il génère son propre code source au lieu de le lire à partir d'un fichier) et calcule en fait tous les nombres qu'il imprime (longueur de code, longueur de sortie, occurrences de caractères). En raison de la façon dont fonctionne le quine, tout le code doit être sur une seule ligne et à l'intérieur d'un littéral de chaîne.

eval r="S='eval r=%p'%r;O=-~$.;q=\"My source has \#{S.size}\"+(X=' characters.\n')+S.chars.uniq.map{|c|[k=S.count(c),k>O ? :are: :is,?\"+c+?\"]*' '}*$/+'\nBesides unquoted numbers, my output has ';r=(w=q+X+s=\"It's good to be a program.\").scan(D=/\\D/).uniq;$><<q<<(w+v=r.map{|c|j=' are \"\n\"';(-~(w+j*r.size).count(c)).to_s+(j[~O]=c;j)}*$/+$/).scan(D).size<<X+v+s"

Sortie partiellement codée en dur, 311 caractères:

La solution la plus courte suivante utilise deux astuces, mais reste une vraie quine: - Aucun caractère n'apparaît exactement une fois dans le code source. De cette façon, je n'ai pas besoin de décider si je dois imprimeris ou aredans la première moitié de la sortie. Cela facilite également un peu le calcul de la taille de sortie totale (même si je n'ai pas vraiment besoin de le faire). - La taille de sortie totale est codée en dur. Comme cela ne dépend que du nombre de caractères distincts dans le code source (et dans le cas général, combien de ces caractères n'apparaissent qu'une seule fois), il est facile de le calculer à l'avance.

Notez que le code est précédé de deux sauts de ligne très importants, que StackExchange ne montrerait pas dans le bloc de code. Pour cette raison, j'ai ajouté une ligne supplémentaire devant si ces nouvelles lignes, qui ne font pas partie du code.

#


eval R="I=$/+$/+'eval R=%p'%R;?\\4>w='%d are \"%s\"';B=\"My source has \#{I.size}\#{X=\" characters.\n\"}\#{z=(m=I.chars.uniq).map{|x|w%[I.count(x),x]}*$/}\nBesides unquoted numbers, my output has 1114\"+X;$><<B+m.map{|c|w%[(B+z+$M=\"\nIt's good to be a program.\").gsub!(/\\d++(?!\")/,'').count(c),c]}*$/+$M"

Solution la plus courte, 269 caractères:

La solution la plus courte code en outre en dur sa propre longueur source. En utilisant des noms de variables qui font / ne font pas déjà partie du code source, il est possible de trouver un "point fixe" où tous les caractères du code source (y compris les chiffres des longueurs codées en dur!) Apparaissent au moins deux fois.

Cette solution enregistre également quelques caractères supplémentaires en lisant simplement son propre code source à partir du fichier de code, au lieu de le générer. Comme un effet secondaire agréable, cela rend le code beaucoup plus "lisible" (mais qui se soucie du code lisible dans un ...), car maintenant le code n'a plus à être à l'intérieur d'un littéral de chaîne.

U='%d are "%s"'
O=IO.read$0
?\126>B="My source has 269#{X=" characters.
"}#{z=(m=O.chars.uniq).map{|c|U%[O.count(c),c]}*$/}
Besides unquoted numbers, my output has 1096"+X
$><<B+m.map{|c|U%[(B+z+$M="
It's good to be a program.").gsub!(/\d++(?!")/,"").count(c),c]}*$/+$M

J'ai également modifié un peu le script de test pour réduire le copier-coller nécessaire pour vérifier le code. En remplaçant les définitions de CODEet OUTPUTpar

import subprocess

CODE = open("packed.rb").read()
OUTPUT = subprocess.check_output(["ruby", "packed.rb"])

print CODE
print len(CODE)

le script exécute maintenant automatiquement mon code, lit sa sortie et récupère le code source du fichier de code.


Voici la sortie générée par le code le plus court:

My source has 269 characters.
3 are "U"
7 are "="
3 are "'"
4 are "%"
6 are "d"
17 are " "
11 are "a"
9 are "r"
9 are "e"
11 are """
11 are "s"
6 are "
"
4 are "O"
2 are "I"
10 are "."
6 are "$"
2 are "0"
2 are "?"
2 are "\"
2 are "1"
2 are "2"
3 are "6"
2 are ">"
4 are "B"
3 are "M"
2 are "y"
9 are "o"
10 are "u"
12 are "c"
4 are "h"
2 are "9"
2 are "#"
4 are "{"
2 are "X"
8 are "t"
4 are "}"
2 are "z"
6 are "("
7 are "m"
5 are "n"
2 are "i"
2 are "q"
6 are ")"
4 are "p"
4 are "|"
2 are "["
4 are ","
2 are "]"
2 are "*"
4 are "/"
3 are "b"
7 are "+"
2 are "<"
3 are "g"
2 are "!"
Besides unquoted numbers, my output has 1096 characters.
2 are "U"
2 are "="
3 are "'"
2 are "%"
5 are "d"
238 are " "
120 are "a"
120 are "r"
120 are "e"
222 are """
11 are "s"
114 are "
"
2 are "O"
3 are "I"
5 are "."
2 are "$"
2 are "0"
2 are "?"
2 are "\"
2 are "1"
2 are "2"
2 are "6"
2 are ">"
3 are "B"
3 are "M"
4 are "y"
9 are "o"
8 are "u"
7 are "c"
6 are "h"
2 are "9"
2 are "#"
2 are "{"
2 are "X"
9 are "t"
2 are "}"
2 are "z"
2 are "("
5 are "m"
4 are "n"
3 are "i"
3 are "q"
2 are ")"
4 are "p"
2 are "|"
2 are "["
3 are ","
2 are "]"
2 are "*"
2 are "/"
4 are "b"
2 are "+"
2 are "<"
4 are "g"
2 are "!"
It's good to be a program.

Pourriez-vous publier une copie définitive de votre code et de votre sortie afin que je puisse facilement le tester? Le code ne doit pas sortir lui-même et la sortie doit se terminer dans une période et non une nouvelle ligne.
Calvin's Hobbies

@ Calvin'sHobbies Le premier bloc de code est mon code réel. Il imprime la sortie avec une nouvelle ligne finale, alors donnez-moi quelques minutes pour corriger cela (c'est quelque chose que vous devez absolument mentionner dans la spécification).
Ventero

Bien sûr, je viens de mettre à jour les spécifications.
Calvin's Hobbies

@ Calvin'sHobbies Done. Le premier bloc de code est le code réel qui est généré par le deuxième bloc de code (de sorte que je n'ai pas à m'occuper de l'échappement des chaînes et de tout lors de l'écriture du code).
Ventero
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.