Auto-méta-code-golf

13

Vous en avez marre de tous les défis du codegolf. Par conséquent, vous décidez d'écrire un programme qui jouera automatiquement du code Python pour vous. Il existe 3 cas de test:

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)

Règles:

  1. Votre programme ne doit pas cibler spécifiquement le code que j'ai publié et doit fonctionner avec n'importe quel code Python 2. Je me réserve le droit de modifier le code source en cours de golf. Vous pouvez supposer qu'il n'y a pas de chaînes multi-lignes (donc vous n'avez pas construit un analyseur complet), et que locals () n'est pas appelé.

  2. La sortie de votre programme doit s'exécuter de la même manière que le code source d'origine. (À savoir, il doit produire la même sortie. Les noms de variables et les constructions de langage peuvent être modifiés, tant que la sortie reste la même)

  3. Vous pouvez utiliser STDIO ou un fichier pour faire votre entrée / sortie du code source.

Votre score sera la somme des octets de la sortie de votre programme.

(Le code ci-dessus provient de http://rosettacode.org/ sous la licence GNU Free Documentation License 1.2 )

Nathan Merrill
la source
3
Voici un cas de test bonus pour les gens à essayer, à être sournois.
Sp3000
4
Quel est votre modèle pour déterminer si la sortie " [s'exécute] de manière identique au code source d'origine "? Par exemple, pour le deuxième exemple, je pense que la suppression if __name__ == '__main__':affecterait le comportement dans certains contextes mais pas dans d'autres. Pour un autre exemple, si l'entrée non golfée suppose qu'elle lit un entier dans stdin et lève un type d'exception si on lui donne autre chose, l'entrée golfée pourrait-elle lancer un type d'exception différent si on lui donne un non entier?
Peter Taylor
2
Qu'en est-il d'un programme comme celui-ci random_long_variable=0;print locals():?
Justin

Réponses:

4

Python 2.7, 794

J'avais l'intention de construire un minifieur pour Python depuis un moment, c'est donc une bonne occasion d'étudier le problème.

Le programme utilise un mélange d'analyse d'expressions régulières et d'opérations d'analyseur Python. L'espace blanc est minimisé. Les variables définies par l'utilisateur sont remplacées par une variable à une seule lettre (qui n'est pas utilisée!). Finalement, lewhile True déclaration est mise sur un régime.

Les trois cas de test vérifient tous qu'ils fonctionnent correctement. Je pourrais imaginer quelques exemples pathologiques qui pourraient entraîner des erreurs dans le code généré mais l'algorithme devrait être robuste dans la plupart des circonstances.

Résultats

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

Production

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))
Logic Knight
la source
4

sed, 1074 (contre 1390)

Type de réponse très doux, à faible pendaison, pour faire rouler la balle:

/^$/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

Remplacer <--TAB-->par de vrais TABpersonnages

Lacune évidente:

  • Les retraits sont supposés avoir exactement 4 espaces dans le code d'entrée.

Comme nous ne pouvons pas supposer de chaînes multi-lignes, nous ne supprimons les espaces de début / fin des opérateurs que s'il n'y en a pas 'ou "sur la ligne donnée. Cela pourrait être amélioré, mais <marmonne quelque chose à propos de regex sed toujours gourmand> .

Testez comme suit:

$ 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
$ 
Traumatisme numérique
la source
Vous n'avez pas besoin de vérifier les chaînes multi-lignes, mais vos deux dernières doivent absolument être mises à jour.
Nathan Merrill
@NathanMerrill yup. L'opérateur qui mène / suit l'espace est un peu mieux maintenant, mais celui en retrait sera beaucoup plus difficile à généraliser - et c'est là que j'obtiens environ les 2/3 du gain.
Digital Trauma
3

Python 3.4, 1134

Ce programme devrait bien fonctionner pour la plupart des programmes. Étrangement, le cas de test Sp3000 est beaucoup plus facile à optimiser pour mon programme que pour vos programmes. L'entrée est acceptée via le fichier spécifié dans le premier argument. Le fichier réel est modifié.

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)

Comment ça fonctionne:

Tout d'abord, ce programme vérifie si votre programme interagit avec l'utilisateur ou utilise au hasard. Si tel est le cas, le programme n'est pas modifié. Ensuite, le programme est exécuté. Le programme est ensuite remplacé parprint "output" . Enfin, si le programme est plus court que sa sortie, il n'est pas modifié.

Le programme du Sp3000, optimisé:

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'

Programme super bonus du Sp3000, optimisé:

La version optimisée n'est désactivée que 0,001% du temps.

import sys
sys.stderr.write(b'')
print b'B\r\n'
Le numéro un
la source
1
Je suis sûr qu'il y a d'autres effets externes que argv, inputet randomque votre code casserait. ;)
Martin Ender
2
Hah. Peut-être que j'aurais dû mettre dans un certain non-déterminisme - print id(0)est un bon.
Sp3000
@Martin Fixed (la plupart du temps). :)
TheNumberOne
Hé, très créatif.
Nathan Merrill