Авто-мета-код-гольф


13

Вам нудно від усіх проблем, кодогольфа. Отже, ви вирішили написати програму, яка автоматично пограє у вас якийсь код Python. Є 3 тестові випадки:

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)

Правила:

  1. Ваша програма не повинна орієнтуватися на код, який я спеціально розмістив, і повинен працювати з будь-яким кодом Python 2. Я залишаю за собою право змінити вихідний код, кодовий гольф. Ви можете припустити, що багаторядкових рядків немає (тому у вас немає побудови повнорозмірного аналізатора), і що локальні () не викликаються.

  2. Вихід вашої програми повинен працювати аналогічно вихідному вихідному коду. (А саме, він повинен виробляти однаковий вихід. Імена змінних та мовні конструкції можна змінювати, доки результат залишається однаковим)

  3. Ви можете використовувати STDIO або файл для введення / виводу вихідного коду.

Ваш результат буде сумою байтів результатів вашої програми.

(Перелічений вище код взятий з http://rosettacode.org/ під ліцензією 1.2 GNU Free Documentation 1.2 )


3
Це здається дуже схожим на codegolf.stackexchange.com/questions/3652/write-a-code-golfer/…
KSab

3
Ось бонусний тестовий випадок, щоб люди спробували, щоб зневажити.
Sp3000

4
Яка ваша модель визначення того, чи буде висновок " [працює] ідентичним чином, як і вихідний вихідний код "? Наприклад, для другого прикладу, я вважаю, що видалення if __name__ == '__main__':вплине на поведінку в деяких контекстах, але не в інших. Для іншого прикладу, якщо невмілене введення припускає, що воно читає int зі stdin і кидає один тип винятку, якщо дано щось інше, чи може введений гольф викинути інший тип винятку, якщо дано не ціле число?
Пітер Тейлор

2
А як щодо такої програми random_long_variable=0;print locals():?
Джастін

Відповіді:


4

Пітон 2.7, 794

Я мав намір створити мініфіксатор для Python на деякий час, тому це хороша можливість дослідити проблему.

Програма використовує поєднання аналізу регулярних виразів та операцій розбору Python. Білий простір зведено до мінімуму. Змінні, що визначені користувачем, замінюються змінною однієї літери (яка не використовується!). Нарешті while Trueзаяву ставлять на дієту.

Усі три тестові справи підтверджують, що вони працюють правильно. Я міг би уявити деякі патологічні приклади, які можуть призвести до помилок у створеному коді, але алгоритм повинен бути надійним за більшості обставин.

Результати

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

Вихідні дані

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)

Код

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

4

sed, 1074 (вниз від 1390)

Дуже м'який, низько висячий плід відповіді, щоб отримати кульку:

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

Замініть <--TAB-->справжніми TABперсонажами

Очевидний недолік:

  • Відступи вважаються рівними 4 пробілами у вхідному коді.

Оскільки ми не можемо вважати відсутність багаторядкових рядків, то ми знімаємо лише провідні / кінцеві пробіли від операторів, якщо таких немає 'або "в даному рядку. Це можна було б покращити, але <нарікає щось на тему, що Sed regex завжди є жадібним> .

Перевірте наступним чином:

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

Вам не потрібно перевіряти багаторядкові рядки, але останні два обов'язково потрібно оновити.
Натан Меррілл

@NathanMerrill yup. Оператор, що веде / простір простору, зараз трохи кращий, але відступ буде набагато складніше узагальнити - і там я отримую приблизно 2/3 виграшу.
Цифрова травма

3

Пітон 3.4, 1134

Ця програма повинна працювати добре для більшості програм. Як не дивно, тестовий випадок Sp3000 значно простіше оптимізувати для моєї програми, ніж ваші програми. Введення приймається через файл, вказаний у першому аргументі. Фактичний файл змінено.

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)

Як це працює:

По-перше, ця програма перевіряє, чи взагалі ваша програма взаємодіє з користувачем чи використовує їх випадково. Якщо це так, програма не змінюється. Далі запускається програма. Потім програма замінюється на print "output". Нарешті, якщо програма коротша, ніж її вихід, вона не змінюється.

Програма Sp3000, оптимізована:

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'

Супер бонусна програма Sp3000, оптимізована:

Оптимізована версія виключена лише .001% часу.

import sys
sys.stderr.write(b'')
print b'B\r\n'

1
Я впевнений, що існують інші зовнішні ефекти, ніж argv, inputі randomякі ваш код порушить. ;)
Мартін Ендер

2
Ха-ха. Можливо, я мав би вкласти якийсь недетермінізм - print id(0)це добре.
Sp3000

@Martin Виправлено (в основному). :)
TheNumberOne


Хе, дуже креативний.
Натан Меррілл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.