Programmazione introspettiva: codice che analizza la sua fonte e il suo output


13

Scrivi un programma che emetta il numero totale di caratteri e la frequenza di ciascun carattere nella sua sorgente e nel suo output. È necessario seguire il formato illustrato nell'esempio.

Esempio

Se il tuo codice era

abb1

Il suo output dovrebbe essere

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.

(L'output deve andare su stdout.)

Si noti, ad esempio, che l'output contiene due m maiuscole. Uno per Mye uno per 2 are "M". Questo deve valere per tutti i caratteri, quindi l'output non si contraddice in alcun modo.

I numeri non quotati vengono ignorati nell'output per evitare insiemi di frequenze insoddisfacenti. Ad esempio, 1 is "1"non è corretto se vengono contati entrambi gli 1. Dovrebbe leggere 2 are "1", ma poi ce n'è di nuovo solo uno.

Chiarimenti sul formato

  • "is" deve essere utilizzato per le occorrenze di singoli caratteri.

  • "are" deve essere utilizzato per più ricorrenze di caratteri.

  • "is" non dovrebbe mai apparire nell'elenco dei caratteri di output perché sarebbe superfluo. 1 is 'Z'si riferisce alla Z in sé, quindi l'intera riga può essere rimossa.

  • Le tre frasi a frase intera devono apparire in ordine con le liste di frequenza dei caratteri tra (come mostra l'esempio). Quindi il tuo output inizierà con My source...e finirà con ...be a program.. Si noti che non esiste una nuova riga alla fine dell'output.

  • Gli elenchi di frequenza dei caratteri stessi possono essere in qualsiasi ordine.

  • Le newline contano come un carattere (nel caso siano \ r \ n).

Controllo formato

Il seguente script Python prende il tuo codice e il suo output come stringhe e afferma che l'output non ha contraddizioni. Fornisce un utile messaggio di errore se qualcosa non va. È possibile eseguirlo online all'indirizzo http://ideone.com/6H0ldu eseguendo il fork, sostituendo le stringhe CODE e OUTPUT, quindi eseguendolo. Non darà mai falsi positivi o negativi (supponendo che sia privo di errori).

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

punteggio

Questo è code-golf. Vince l'invio con il minor numero di personaggi. Gli invii devono superare il controllo del formato per essere validi. Si applicano scappatoie standard, sebbene sia possibile leggere il proprio codice sorgente e / o codificare il proprio output .


È consentita la lettura del proprio file sorgente?
Ventero,

@MrLore Potrebbero esserci altri errori ma mi sono appena reso conto che le virgolette triple ('' ') sfuggono ancora alle cose con la barra rovesciata. Questo potrebbe essere correlato al tuo problema. Lo sto riparando ora.
Calvin's Hobbies,

@Ventero Definitely!
Calvin's Hobbies,

@MrLore Le regexps consentono alcuni falsi positivi, sì. Per risolvere il problema con le barre rovesciate tra virgolette triple, utilizzare raw stringhe ( r'''CODE''').
Ventero,

1
@MrLore Risolti i punti senza caratteri di escape. Grazie per averlo notato!
Calvin's Hobbies,

Risposte:


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

Provalo su http://cjam.aditsu.net/

Produzione:

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

Rubino, 269 (311, 367) caratteri

Ho tre diverse soluzioni per questa sfida. Ognuno di loro utilizza una diversa serie di trucchi:

Soluzione "corretta", 367 caratteri:

La soluzione più lunga è più o meno solo una prova del concetto che è possibile risolvere questa sfida senza trucchi - e non è quasi completamente golfato. È un vero quine (cioè genera il proprio codice sorgente invece di leggerlo da un file) e in realtà calcola tutti i numeri che stampa (lunghezza del codice, lunghezza dell'output, occorrenze dei caratteri). A causa del modo in cui funzionano le quine, tutto il codice deve essere su una sola riga e all'interno di una stringa letterale.

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"

Uscita parzialmente codificata, 311 caratteri:

La soluzione più breve successiva utilizza due trucchi, ma è ancora una vera quine: - Nessun carattere appare esattamente una volta nel codice sorgente. In questo modo, non ho bisogno di decidere se stampare iso arenella prima metà dell'output. Inoltre, rende un po 'più semplice il calcolo della dimensione totale dell'output (anche se in realtà non è necessario). - La dimensione totale dell'output è codificata. Poiché ciò dipende solo dal numero di caratteri distinti nel codice sorgente (e, nel caso generale, da quanti di questi caratteri compaiono una sola volta), è facile calcolarlo in anticipo.

Si noti che il codice è preceduto da due newline molto significative, che StackExchange non mostrerebbe nel blocco di codice. Per questo motivo, ho aggiunto una riga aggiuntiva di fronte a quelle nuove righe, che non fa parte del codice.

#


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"

Soluzione più breve, 269 caratteri:

La soluzione più breve inoltre codifica la propria lunghezza sorgente. Usando i nomi delle variabili che sono / non sono già parte del codice sorgente, è possibile trovare un "punto fisso" in cui tutti i caratteri nel codice sorgente (comprese le cifre dalle lunghezze codificate!) Si presentano almeno due volte.

Questa soluzione salva anche alcuni caratteri in più semplicemente leggendo il proprio codice sorgente dal file di codice, invece di generarlo. Come un piacevole effetto collaterale, questo rende il codice molto più "leggibile" (ma a chi importa del codice leggibile in un ...), poiché ora il codice non deve più essere all'interno di una stringa letterale.

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

Ho anche modificato un po 'lo script di test per ridurre il copia-incolla necessario per controllare il codice. Sostituendo le definizioni di CODEe OUTPUTcon

import subprocess

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

print CODE
print len(CODE)

lo script ora esegue automaticamente il mio codice, legge il suo output e prende il codice sorgente dal file di codice.


Ecco l'output generato dal codice più breve:

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.

Potresti pubblicare una copia definitiva del codice e dell'output in modo da poterlo testare facilmente? Il codice non dovrebbe generare se stesso e l'output dovrebbe terminare in un periodo non in una nuova riga.
Calvin's Hobbies,

@ Calvin'sHobbies Il primo blocco di codice è il mio codice attuale. Tuttavia stampa l'output con una nuova riga finale, quindi dammi qualche minuto per risolverlo (questo è qualcosa che dovresti assolutamente menzionare nelle specifiche).
Ventero,

Certo, ho appena aggiornato le specifiche.
Calvin's Hobbies,

@ Calvin'sHobbies Done. Il primo blocco di codice è il codice effettivo generato dal secondo blocco di codice (quindi non devo occuparmi dell'escaping della stringa e di tutto mentre scrivo il codice).
Ventero,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.