Auto-Meta-Code-Golf

13

Sie haben alle Codegolf-Herausforderungen satt. Daher beschließen Sie, ein Programm zu schreiben, das automatisch Python-Code für Sie spielt. Es gibt 3 Testfälle:

print quickSort([0,7,3,-1,8,10,57,2])
def quickSort(arr):
    less = []
    pivotList = []
    more = []
    if len(arr) <= 1:
        return arr
    else:
        pivot = arr[0]
        for i in arr:
            if i < pivot:
                less.append(i)
            elif i > pivot:
                more.append(i)
            else:
                pivotList.append(i)
        less = quickSort(less)
        more = quickSort(more)
        return less + pivotList + more

for i in xrange(1, 101):
    if i % 15 == 0:
        print "FizzBuzz"
    elif i % 3 == 0:
        print "Fizz"
    elif i % 5 == 0:
        print "Buzz"
    else:
        print i

from sys import argv

def randomGenerator(seed=1):
    max_int32 = (1 << 31) - 1
    seed = seed & max_int32

    while True:
        seed = (seed * 214013 + 2531011) & max_int32
        yield seed >> 16

def deal(seed):
    nc = 52
    cards = range(nc - 1, -1, -1)
    rnd = randomGenerator(seed)
    for i, r in zip(range(nc), rnd):
        j = (nc - 1) - r % (nc - i)
        cards[i], cards[j] = cards[j], cards[i]
    return cards

def show(cards):
    l = ["A23456789TJQK"[c / 4] + "CDHS"[c % 4] for c in cards]
    for i in range(0, len(cards), 8):
        print " ", " ".join(l[i : i+8])

if __name__ == '__main__':
    seed = int(argv[1]) if len(argv) == 2 else 11982
    print "Hand", seed
    deck = deal(seed)
    show(deck)

Regeln:

  1. Ihr Programm darf nicht auf den von mir angegebenen Code abzielen und sollte mit jedem Python 2-Code funktionieren. Ich behalte mir das Recht vor, den Quellcode zu ändern, der codegolfed wird. Sie können davon ausgehen, dass es keine mehrzeiligen Zeichenfolgen gibt (Sie haben also keinen vollständigen Parser erstellt), und dass locals () nicht aufgerufen wird.

  2. Die Ausgabe Ihres Programms sollte auf die gleiche Weise wie der ursprüngliche Quellcode ausgeführt werden. (Es muss nämlich die gleiche Ausgabe erzeugen. Variablennamen und Sprachkonstrukte können geändert werden, solange die Ausgabe dieselbe bleibt.)

  3. Sie können STDIO oder eine Datei verwenden, um den Quellcode einzugeben / auszugeben.

Ihre Punktzahl ist die Summe der Bytes der Programmausgabe.

(Der oben aufgeführte Code wurde von http://rosettacode.org/ unter der GNU Free Documentation License 1.2 entnommen. )

Nathan Merrill
quelle
3
Hier ist ein Bonus-Testfall für Leute, die es versuchen sollen, verschlagen zu sein.
Sp3000,
4
Nach welchem ​​Modell können Sie feststellen, ob die Ausgabe " [läuft] auf die gleiche Weise wie der ursprüngliche Quellcode "? ZB für das zweite Beispiel glaube ich, dass das Entfernen if __name__ == '__main__':das Verhalten in einigen Kontexten beeinflussen würde, in anderen jedoch nicht. Wenn in einem anderen Beispiel die Eingabe "Ungolfed" davon ausgeht, dass sie ein Int aus "stdin" liest und einen Ausnahmetyp auslöst, wenn etwas anderes angegeben ist, kann die Eingabe "Golfed" dann einen anderen Ausnahmetyp auslösen, wenn eine Nicht-Ganzzahl angegeben ist?
Peter Taylor
2
Was ist mit einem Programm wie diesem random_long_variable=0;print locals()?
Justin

Antworten:

4

Python 2.7, 794

Ich wollte schon seit einiger Zeit einen Minifier für Python erstellen, daher ist dies eine gute Gelegenheit, das Problem zu untersuchen.

Das Programm verwendet eine Mischung aus der Analyse regulärer Ausdrücke und Python-Parser-Operationen. Leerraum wird minimiert. Vom Benutzer definierte Variablen werden durch eine einzelne Buchstabenvariable (die nicht verwendet wird!) Ersetzt. Schließlich wird die while TrueAussage auf eine Diät gesetzt.

Alle drei Testfälle verifizieren, dass sie korrekt ausgeführt werden. Ich könnte mir einige pathologische Beispiele vorstellen, die zu Fehlern im generierten Code führen könnten, aber der Algorithmus sollte unter den meisten Umständen robust sein.

Ergebnisse

228 t1.py
128 t2.py
438 t3.py
794 total

Ausgabe

def c(a):
 b=[]
 d=[]
 f=[]
 if len(a)<=1:
  return a
 else:
  e=a[0]
  for i in a:
   if i<e:
    b.append(i)
   elif i>e:
    f.append(i)
   else:
    d.append(i)
  b=c(b)
  f=c(f)
  return b+d+f
print c([0,7,3,-1,8,10,57,2])


for i in xrange(1,101):
 if i%15==0:
  print"FizzBuzz"
 elif i%3==0:
  print"Fizz"
 elif i%5==0:
  print"Buzz"
 else:
  print i


from sys import argv
def a(k=1):
 b=(1<<31)-1
 k=k&b
 while 1:
  k=(k*214013+2531011)&b
  yield k>>16
def d(k):
 f=52
 h=range(f-1,-1,-1)
 g=a(k)
 for i,r in zip(range(f),g):
  j=(f-1)-r%(f-i)
  h[i],h[j]=h[j],h[i]
 return h
def m(h):
 l=["A23456789TJQK"[c/4]+"CDHS"[c%4]for c in h]
 for i in range(0,len(h),8):
  print" "," ".join(l[i:i+8])
if __name__=='__main__':
 k=int(argv[1])if len(argv)==2 else 11982
 print"Hand",k
 e=d(k)
 m(e)

Code

import sys
import re
from tokenize import generate_tokens
from token import tok_name
from keyword import iskeyword

wr = sys.stdout.write

def pyparse(text):
    'Return [TYPE,TOKEN] pair list'
    # Use KEYWORD,NAME,NUMBER,OP,STRING,NL,NEWLINE,COMMENT,INDENT,DEDENT
    rawtokens = generate_tokens(text.readline)
    tokens = [[tok_name[n], t] for n,t,p1,p2,dx in rawtokens]
    for tpair in tokens:
        if tpair[0] == 'NAME' and iskeyword(tpair[1]):
            tpair[0] = 'KEYWORD'
    return tokens

def finduservars(filename):
    'Return a set of user variables that we can replace with a-zA-Z'
    varset = set()
    for line in open(filename):
        line = line.strip()
        match = re.match(r'def\s+(\w+)\s*\((.*)\)\s*:', line)
        if match:
            func, args = match.groups()
            varset.add(func)
            arglist = re.findall(r'(\w+|=)', args)
            for a in arglist:
                if a == '=':
                    break  # keyword args follow - too hard to parse
                varset.add(a)
            continue
        match = re.match(r'(\w+)\s*=.+', line)
        if match:
            assigned = match.group(1)
            varset.add(assigned)
            continue
    return set(v for v in list(varset) if len(v) > 1)

filename = sys.argv[1]
tokenlist = pyparse(open(filename))

# Build map for var->char conversion:
varset = finduservars(filename)
singles = [text for tok,text in tokenlist if tok=='NAME' and len(text)==1]
allvar = [chr(n) for n in range(97,123)+range(65,91)]
charvar = [c for c in allvar if c not in singles]
varreplaced = list(varset)[:len(charvar)]
varmap = dict((v, charvar.pop(0)) for v in varreplaced)

prev = 'NONE'
indent = ['']
output = []
add = output.append
for tok, text in tokenlist:
    if tok == 'NL':
        continue
    elif tok == 'INDENT':
        indent.append( text.replace('    ', ' ') )
        output[-1] = indent[-1]
    elif tok == 'DEDENT':
        indent.pop(-1)
        output[-1] = indent[-1]
    elif tok == 'NEWLINE':
        add(text)
        add(indent[-1])
    elif tok in 'KEYWORD,NAME,NUMBER':
        if prev in 'KEYWORD,NAME,NUMBER':
            add(' ')
        if tok == 'NAME':
            if output[-2] == 'while' and text == 'True':
                add('1') # common verbose idiom
            else:
                add(varmap.get(text, text))
        else:
            add(text)
    else:
        add(text)
    prev = tok

wr(''.join(output))
Logik-Ritter
quelle
4

sed, 1074 (nach 1390)

Sehr milde, niedrig hängende Frucht-Antwort, um den Ball ins Rollen zu bringen:

/^$/d                  # Remove empty lines
/^[ <--TAB-->]*#/d     # Remove whole-line comments
s/    /<--TAB-->/g     # Replace 4 spaces with tabs
/^[^'"]*$/s/ *([|&:,<>=*/%+-]) */\1/g  # Remove spaces before/after operators

Ersetzen Sie <--TAB-->durch echte TABZeichen

Offensichtliches Manko:

  • Einrückungen werden als genau 4 Leerzeichen im Eingabe-Code angenommen.

Da wir keine mehrzeiligen Zeichenfolgen annehmen können, entfernen wir führende / nachfolgende Leerzeichen von Operatoren nur, wenn keine 'oder "in der angegebenen Zeile vorhanden sind. Dies könnte verbessert werden, aber <murmelt etwas darüber, dass sed regex immer gierig ist> .

Testen Sie wie folgt:

$ cat qs.py fizzbuzz.py cards.py | wc -c
1390
$ sed -rf pygolf.sed qs.py fizzbuzz.py cards.py | wc -c
1074
$ sed -rf pygolf.sed qs.py fizzbuzz.py cards.py | python
[-1, 0, 2, 3, 7, 8, 10, 57]
1
2
Fizz
...
98
Fizz
Buzz
Hand 11982
  AH AS 4H AC 2D 6S TS JS
  3D 3H QS QC 8S 7H AD KS
  KD 6H 5S 4D 9H JH 9S 3C
  JC 5D 5C 8C 9D TD KH 7C
  6C 2C TH QH 6D TC 4S 7S
  JD 7D 8H 9C 2H QD 4C 5H
  KC 8D 2S 3S
$ 
Digitales Trauma
quelle
Sie müssen nicht nach mehrzeiligen Zeichenfolgen suchen, aber die letzten beiden müssen unbedingt aktualisiert werden.
Nathan Merrill
@ NathanMerrill yup. Der Operator Leading / Trailing Space One ist jetzt ein bisschen besser, aber der Indent One wird viel schwieriger zu verallgemeinern sein - und hier bekomme ich ungefähr 2/3 des Gewinns.
Digital Trauma
3

Python 3.4, 1134

Dieses Programm sollte für die meisten Programme in Ordnung sein. Seltsamerweise ist der Sp3000-Testfall für mein Programm viel einfacher zu optimieren als für Ihre Programme. Die Eingabe wird über die im ersten Argument angegebene Datei akzeptiert. Die eigentliche Datei wird geändert.

import subprocess
from sys import argv

progamtext = open(argv[1]).read()

if 'argv' in progamtext or 'input' in progamtext or 'open' in programtext:#Make sure the program always produces the same results.
    exit(0)

program = subprocess.Popen(['C:\Python27\python', argv[1]], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
program.wait()
erroroutput1 = str(program.stderr.read())
output1 = str(program.stdout.read())
program = subprocess.Popen(['C:\Python27\python', argv[1]], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
program.wait()
erroroutput2 = str(program.stderr.read())
output2 = str(program.stdout.read())
if erroroutput1 != erroroutput2 or output1 != output2:#Make sure the program always produces the same results.
    exit(0)

newprog = ''
if erroroutput1:
    newprog += "import sys\n" + "sys.stderr.write("+ erroroutput1 + ')'
    if output1:
        newprog += "\n"
if output1:
    newprog += 'print ' + output1

if len(newprog) > len(progamtext):
    exit(0)

open(argv[1],mode='w').write(newprog)

Wie es funktioniert:

Zuerst prüft dieses Programm, ob Ihr Programm überhaupt mit dem Benutzer interagiert oder zufällig verwendet. In diesem Fall bleibt das Programm unverändert. Als nächstes wird das Programm ausgeführt. Das Programm wird dann durch ersetzt print "output". Wenn das Programm kürzer als die Ausgabe ist, bleibt es unverändert.

Das Programm von Sp3000, optimiert:

import sys
sys.stderr.write(b'')
print b'0.540377721372\r\n3\r\n1\r\n7\r\n99\r\nf\r\n[5, 5]\r\n53\r\n53\r\n53\r\n'

Das Super-Bonusprogramm von Sp3000, optimiert:

Die optimierte Version ist nur in 0,001% der Fälle deaktiviert.

import sys
sys.stderr.write(b'')
print b'B\r\n'
Die Nummer eins
quelle
1
Ich bin sicher , es gibt auch andere externe Effekte als argv, inputund random, die Ihr Code brechen würde. ;)
Martin Ender
2
Hah Vielleicht hätte ich einen Nicht-Determinismus einsetzen sollen - print id(0)ist ein guter.
Sp3000
@Martin Behoben (meistens). :)
TheNumberOne
Sehr kreativ.
Nathan Merrill