Introspektive Programmierung: Code, der seine Quelle und seine Ausgabe analysiert

13

Schreiben Sie ein Programm, das die Gesamtzahl der Zeichen und die Häufigkeit jedes Zeichens in seiner Quelle ausgibt und seiner Ausgabe ausgibt. Sie müssen das im Beispiel gezeigte Format befolgen.

Beispiel

Wenn Ihr Code war

abb1

Seine Ausgabe müsste sein

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.

(Die Ausgabe muss auf stdout gehen.)

Beachten Sie beispielsweise, dass die Ausgabe zwei großgeschriebene m enthält. Eins für Myund eins für 2 are "M". Dies muss für alle Zeichen zutreffen, damit die Ausgabe sich in keiner Weise widerspricht.

Nicht zitierte Zahlen werden in der Ausgabe ignoriert, um nicht erfüllbare Frequenzsätze zu vermeiden. 1 is "1"Ist beispielsweise falsch, wenn beide Einsen gezählt werden. Es sollte lauten 2 are "1", aber dann gibt es wieder nur eine 1.

Formaterklärungen

  • "is" muss für einzelne Zeichen verwendet werden.

  • "are" muss für mehrere Zeichen verwendet werden.

  • "is" sollte niemals in der Liste der Ausgabezeichen erscheinen, da dies überflüssig wäre. 1 is 'Z'Bezieht sich auf das Z an sich, so kann die gesamte Zeile entfernt werden.

  • Die drei vollständigen Sätze müssen in der Reihenfolge erscheinen, in der die Buchstabenhäufigkeitslisten dazwischen liegen (wie das Beispiel zeigt). Ihre Ausgabe beginnt also mit My source...und endet mit ...be a program.. Beachten Sie, dass am Ende der Ausgabe kein Zeilenumbruch steht.

  • Die Zeichenhäufigkeitslisten selbst können in beliebiger Reihenfolge sein.

  • Zeilenumbrüche zählen als ein Zeichen (falls sie \ r \ n sind).

Format Checker

Das folgende Python-Skript nimmt Ihren Code und seine Ausgabe als Zeichenfolgen und behauptet, dass die Ausgabe keine Widersprüche enthält. Es gibt eine nützliche Fehlermeldung aus, wenn etwas nicht stimmt. Sie können es online unter http://ideone.com/6H0ldu ausführen, indem Sie es forken, die CODE- und OUTPUT-Zeichenfolgen ersetzen und dann ausführen. Es wird niemals falsch positive oder negative Ergebnisse liefern (vorausgesetzt, es ist fehlerfrei).

#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()

Wertung

Das ist Code-Golf. Die Einsendung mit den wenigsten Zeichen gewinnt. Einsendungen müssen die Formatprüfung bestehen, um gültig zu sein. Es gelten Standardlücken, obwohl Sie Ihren eigenen Quellcode lesen und / oder Ihre Ausgabe fest codieren können .

Calvins Hobbys
quelle
Ist das Lesen Ihrer eigenen Quelldatei erlaubt?
Ventero
@MrLore Möglicherweise gibt es andere Fehler, aber ich habe gerade festgestellt, dass die dreifachen Anführungszeichen ('' ') die Dinge immer noch mit Backslash umgehen. Dies hängt möglicherweise mit Ihrem Problem zusammen. Ich repariere es jetzt.
Calvins Hobbys
@Ventero Auf jeden Fall!
Calvins Hobbys
@MrLore Die regulären Ausdrücke erlauben einige falsche Positive, ja. Verwenden Sie rohe Zeichenfolgen ( r'''CODE'''), um das Problem mit Backslashes in dreifachen Anführungszeichen zu beheben .
Ventero
1
@MrLore Unescaped Dots behoben. Danke fürs bemerken!
Calvins Hobbys

Antworten:

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];}_~

Versuchen Sie es unter http://cjam.aditsu.net/

Ausgabe:

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.
aditsu
quelle
11

Ruby, 269 (311, 367) Zeichen

Ich habe drei verschiedene Lösungen für diese Herausforderung. Jeder von ihnen verwendet unterschiedliche Tricks:

"Richtige" Lösung, 367 Zeichen:

Die längste Lösung ist mehr oder weniger nur ein Proof of Concept, dass es möglich ist, diese Herausforderung ohne Tricks zu lösen - und sie ist bei weitem nicht ausgereift. Es ist ein echtes Quine (dh es generiert seinen eigenen Quellcode, anstatt ihn aus einer Datei zu lesen) und berechnet tatsächlich alle von ihm ausgegebenen Zahlen (Codelänge, Ausgabelänge, Zeichenvorkommen). Aufgrund der Funktionsweise des Quines muss sich der gesamte Code in einer einzigen Zeile und in einem String-Literal befinden.

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"

Teilweise fest codierte Ausgabe, 311 Zeichen:

Die nächstkürzere Lösung verwendet zwei Tricks, ist aber immer noch eine echte Quine: - Kein Zeichen kommt genau einmal im Quellcode vor. Auf diese Weise muss ich mich nicht entscheiden, ob ich drucken soll isoder nichtare in der ersten Hälfte der Ausgabe . Es macht es auch ein bisschen einfacher, die Gesamtausgabegröße zu berechnen (obwohl ich das eigentlich nicht tun muss). - Die Gesamtgröße der Ausgabe ist fest codiert. Da dies nur von der Anzahl der unterschiedlichen Zeichen im Quellcode abhängt (und im Allgemeinen davon, wie viele dieser Zeichen nur einmal vorkommen), ist es einfach, sie im Voraus zu berechnen.

Beachten Sie, dass dem Code zwei sehr wichtige Zeilenvorschübe vorangestellt sind, die StackExchange im Codeblock nicht anzeigen würde. Aus diesem Grund habe ich vor den Zeilenumbrüchen eine zusätzliche Zeile eingefügt, die nicht Teil des Codes ist.

#


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"

Kürzeste Lösung, 269 Zeichen:

Die kürzeste Lösung kodiert zusätzlich die eigene Quelllänge fest. Durch die Verwendung von Variablennamen, die bereits Teil des Quellcodes sind oder nicht, ist es möglich, einen "Fixpunkt" zu finden, an dem alle Zeichen im Quellcode (einschließlich der Ziffern aus den fest codierten Längen!) Mindestens zweimal vorkommen.

Diese Lösung spart auch ein paar weitere Zeichen, indem Sie einfach den eigenen Quellcode aus der Codedatei lesen, anstatt ihn zu generieren. Als netter Nebeneffekt macht dies den Code viel "lesbarer" (aber wer kümmert sich schon um lesbaren Code in einem ...), da sich der Code jetzt nicht mehr in einem String-Literal befinden muss.

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

Ich habe auch das Testskript ein wenig modifiziert, um das zum Überprüfen des Codes erforderliche Kopieren und Einfügen zu reduzieren. Durch Ersetzen der Definitionen von CODEund OUTPUTdurch

import subprocess

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

print CODE
print len(CODE)

Das Skript führt jetzt automatisch meinen Code aus, liest seine Ausgabe und holt den Quellcode aus der Codedatei.


Hier ist die Ausgabe, die vom kürzesten Code generiert wird:

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.
Ventero
quelle
Könnten Sie eine endgültige Kopie Ihres Codes veröffentlichen und ausgeben, damit ich ihn problemlos testen kann? Der Code sollte sich nicht selbst ausgeben und die Ausgabe sollte in einem Punkt enden, der keine neue Zeile ist.
Calvins Hobbys
@ Calvin'sHobbies Der erste Codeblock ist mein eigentlicher Code. Die Ausgabe wird jedoch mit einer letzten neuen Zeile gedruckt. Geben Sie mir daher ein paar Minuten Zeit, um das zu beheben (dies ist etwas, das Sie in der Spezifikation unbedingt erwähnen sollten).
Ventero
Klar, ich habe gerade die Spezifikation aktualisiert.
Calvins Hobbys
@ Calvin'sHobbies Fertig. Der erste Codeblock ist der eigentliche Code, der vom zweiten Codeblock generiert wird (damit ich beim Schreiben des Codes nicht auf das Entstehen von Zeichenfolgen und alles achten muss).
Ventero