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

13

Вы устали от всех проблем Codegolf. Следовательно, вы решили написать программу, которая автоматически добавит вам код 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. Я оставляю за собой право вносить изменения в исходный код. Вы можете предположить, что многострочных строк нет (поэтому вам не нужно создавать полноценный парсер), и что localals () не вызывается.

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

  3. Вы можете использовать STDIO или Файл для ввода / вывода исходного кода.

Ваша оценка будет суммой байтов вывода вашей программы.

(Код, указанный выше, был взят с http://rosettacode.org/ под лицензией GNU Free Documentation License 1.2 )

Натан Меррилл
источник
3
Это выглядит очень похоже на codegolf.stackexchange.com/questions/3652/write-a-code-golfer/…
KSab
3
Вот бонус-тест для людей, чтобы попробовать, чтобы быть хитрым.
Sp3000
4
Какова ваша модель для определения того, «выводится ли« [запускается] идентично ли исходному коду »? Например, для второго примера, я считаю, что удаление if __name__ == '__main__':повлияет на поведение в некоторых контекстах, но не в других. В другом примере, если вход ungolfed предполагает, что он читает int из stdin и генерирует исключение одного типа, если ему дано что-то другое, может ли ввод с вводом в гольф генерировать исключение другого типа, если ему дано нецелое число?
Питер Тейлор
2
Как насчет такой программы random_long_variable=0;print locals()?
Джастин

Ответы:

4

Python 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

Сед, 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 да. Оператор, ведущий / завершающий пробел один, теперь немного лучше, но отступ будет сложнее обобщать - и именно здесь я получаю около 2/3 от выигрыша.
Цифровая травма
3

Python 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
Хех, очень креативно.
Натан Меррилл