Napiszmy Minifier

14

tło

Minifikatory są zwykle używane podczas obsługi JavaScript w przeglądarce. Jest powszechnie używany do zmniejszenia liczby bajtów, które należy wysłać. Oszczędzanie przepustowości jest przydatne z oczywistych powodów. Niektórzy używają obfuscaters (które celowo utrudniają czytanie kodu), nie mówię o nich.

Będziemy minimalizować Python 2

Zastanawiałem się nad tym, czy używać JavaScript lub Pythona do minimalizowania doświadczeń i zdecydowałem się na Python z dwóch powodów: biała przestrzeń ma znaczenie i myślę, że doda to interesującą dynamikę problemu. Dodatkowo, użycie Python 2.7 zapewni kolejną dynamikę, taką jak usunięcie zbędnych ()podczas drukowania (tj. print("Hello world")Vs. print"Hello world"). Wolałbym osobiście otworzyć go na dowolny język, ale w przypadku niektórych języków ten proces nie ma większego sensu. A język, który zdecydujesz się zminimalizować, będzie miał bezpośredni wpływ na twój wynik (i czy język można nawet zminimalizować).

Okular

Twoim celem jest modyfikacja kodu tylko w sposób, który i tak nie zmieni jego funkcjonalności. Możesz oczywiście zmieniać nazwy zmiennych (w programie minimalizującym), o ile nie wpływa to na wynik (śledzenie zakresu ). Chociaż daję ci konkretny program, nie optymalizuj dla przypadku testowego, ponieważ wszystkie standardowe luki są zabronione.

Wynik : długość programu po jego zminimalizowaniu.

Dane wejściowe : dowolny program Python 2.7 (który nie zawiera błędów)

Wyjście : wersja zminimalizowana.

Chociaż twój kod powinien być w stanie pomieścić wszystkie prawidłowe dane wejściowe Python 2.7, konieczne jest przetestowanie skryptu na czymś, aby udowodnić jego skuteczność.

Kliknij tutaj, aby wyświetlić przykładowy program.

Sprawienie, by problem był bardziej przystępny

Zapraszam do używania lub modyfikowania dowolnego kodu znajdującego się w moim rozwiązaniu (wymienionym poniżej). Zrobiłem to, aby zacząć od wyceny podstawowej obsługi wyceny; możesz jednak rozszerzyć go na wcięcie itp.

Przykładowe sposoby zminimalizowania Pythona

Cała biała spacja może zostać zastąpiona minimalną możliwą ilością (potwierdzam, że w Pythonie możesz robić skomplikowane rzeczy za pomocą zakładek , ale pozostawiam to tobie, aby zdecydować, czy je wdrożyć, czy nie).

Przykład

Następujące:

def print_a_range(a):
    for i in range(a):
        print(i)

Możliwe:

def print_a_range(a):
 for i in range(a):
  print(i)

Technicznie, jeśli w pętli jest tylko jedna linia, możesz ją jeszcze bardziej skompresować:

def print_a_range(a):
 for i in range(a):print(i)  #Note, you can also remove the `()` here.

Istnieje jednak inny sposób zmniejszenia białych znaków w Pythonie:

Następujące:

print ([a * 2 for a in range(20) if a % 2 == 0])

Możliwe:

print([a*2for a in range(20)if a%2==0])

Zauważ, że nie ma potrzeby odstępu między 2i for. Zmienna, funkcje i słowa kluczowe nie mogą zaczynać się od cyfry. Tak więc interpreter języka Python jest w porządku <num><keyword>, bez spacji. Należy również zauważyć, że nie ma spacji między )i if.

Uwaga: nie wolno zmieniać wyniku działania programu! Więc:

print"f(x)=x*2 is a great equation!"

Powyższa instrukcja print powinna pozostać niezmieniona, ponieważ usunięcie spacji między 2i iszmodyfikowałoby dane wyjściowe.

Neil
źródło
Uwaga: w tej dyskusji nie ma programu, który mógłby wygenerować najkrótszy odpowiednik dowolnego programu do wprowadzania danych
Leaky Nun,
Istnieją już narzędzia minimalizatora Pythona . Nie sądzę, że to pytanie może otrzymać lepsze rozwiązanie niż narzędzia wyjścia.
tsh
Czy zmiana '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'na '1'*100dozwoloną? Wymagać zrobienia, ponieważ zachowanie jest takie samo?
l4m2

Odpowiedzi:

2

Wynik w Python 2.7, 2013

Ten program może być używany jako odniesienie i możesz wziąć następujący kod i zmodyfikować go, a następnie opublikować we własnych rozwiązaniach.

Z perspektywy czasu być może powinienem również użyć wyrażenia regularnego do obsługi cytatów, ale myślę, że w obecnym stanie może wystarczyć, aby zachęcić ludzi do rozwiązania problemu.

Dlaczego wybrałem Python 2.7: Pomyślałem, że łatwiej będzie przetestować, czy spowodowałem awarię programu za pomocą execsłowa kluczowego.

Ten kod przyjmuje program jako in.txt.

Uznałem, że powinienem przynajmniej rzucić piłkę dla każdego, kto chce wziąć udział, pisząc parser cytatów (który również zajmuje się komentarzami) i krótki przykład tego, jak wyrażenie regularne, w połączeniu z parserem cytatów, może naprawdę zmienić grę pod względem złożoność tego problemu.

Uwaga: w tym minifikatorze wciąż jest dużo miejsca na ulepszenia. Tak jak możesz bawić się wcięciami, nazwami zmiennych i usuwaniem nawiasów, gdy są używane, moje słowa kluczowe, takie jak printlub yield.

import re

with open("in.txt","r") as fi:
    code = fi.read()

class QuoteHandler():
    def __init__(self):
        pass
    def loadCode(self,code):
        quoteFlag = False
        currentQuoteChar = ""
        ignoreNext = False
        inEndLineComment=False
        startLocation = 0

        self.reAddStrings = []

        outStr = ""

        for i, character in enumerate(code):
            if ignoreNext:
                ignoreNext = False
            elif inEndLineComment:
                if character in "\r\n":
                    inEndLineComment=False
            elif character == "#" and not quoteFlag:
                inEndLineComment = True
            elif character in "'\"" and (currentQuoteChar == character or not quoteFlag):
                if quoteFlag:
                    self.reAddStrings.append(code[startLocation+1:i])
                else:
                    currentQuoteChar = character
                    startLocation = i
                quoteFlag = not quoteFlag
            elif character == "\\":
                ignoreNext = True

            if not inEndLineComment and not quoteFlag:
                outStr+=character                
        return outStr

    def find_all_locations(self,substr,code):
        return [m.start() for m in re.finditer(substr, code)]

    def unloadCode(self,code):
        temp = self.reAddStrings[::-1]
        for i, location in enumerate(list(self.find_all_locations('"',code))[::-1]):
            code = code[:location] + "\"" + temp[i] + code[location:]
        return code

def applyRegexes(code):#\w here?
    operatorRegexCleaner = ["([\d\/*\-\"=,'+{}:[\](\)])","[ \t]+","(\w)"]
    regexes = [
        [''.join(operatorRegexCleaner),r"\1\2"],
        [''.join(operatorRegexCleaner[::-1]),r"\1\2"],#removes whitespace between operators
        ["\n\s*\n","\n"]#removes empty lines
    ]
    for regex in regexes:
        code = re.sub(regex[0],regex[1],code)
    return code

qh = QuoteHandler()
code = qh.loadCode(code)
code = applyRegexes(code)
code = qh.unloadCode(code)
print(code)
exec(code)

Wyjście programu:

def factor(factor_number):
    for n in range(2,factor_number):
        if factor_number % n==0:    
            yield(n)
def gcd(a,b):
    """Calculate the Greatest Common Divisor of a and b.

    Unless b==0, the result will have the same sign as b (so that when
    b is divided by it, the result comes out positive).
    """
    while b:
         a,b=b,a%b 
    return a
class Apricot:
    def __init__(self):
        self.mold=False
    def get(self):
        return self.mold
    def update(self):
        self.mold=not self.mold
    def blue(self):return5
def tell_me_about_these_numbers(*a):
    print("%d is the first number!" % a[0])
    print("{} / 3 is {}".format(a[0],a[0]/3.))
    myFavorate=Apricot()
    for number in a:
        print list(factor(number))
        myFavorate.update()
    print[gcd(a,b)for a,b in zip(a[:-1],a[1:])]
    print(myFavorate.get())
tell_me_about_these_numbers(5,6,9,45,200)
print"Let's play with scope!"
a,b=10,9
def randomFunction(a):
    print(a)
randomFunction(b)
print(a)
for a in range(100):
    b+=a
print(a)
print(b)
li=[]
for i in range(10):
 li.append(i*2)
print(li)
print([i*2for i in range(10)])
a=c=b=d=e=f=g=h=i=j=k=l=m=n=o=p=q=r=s=t=u=v=w=x=y=z=5
print(a)
a-=1
print(a)
g=10
print(str(10**g+5)[::-1])
def blue_fish(a):
    def blue_fish(a):
        def blue_fish(a):
            return a
        a+=1
        return blue_fish(a)
    a-=1
    return blue_fish(a)
print(blue_fish(10))
def blue_fish(a):
    if a==0:
        return"0"
    return"1" +blue_fish(a-1)
print(blue_fish(5))
blue_fish=lambda a,b,c:a*b*c
print(blue_fish(1,2,3))
blue_fish=lambda*a:reduce(lambda a,b:a*b,a)
print(blue_fish(1,2,3))
print(max([[6,1],[5,2],[4,3],[3,4],[2,5],[1,6]],key=lambda a:a[1]))
print(zip(*[[1],[2],[3],[4],[5]]))
print"Now let's test to see if you handle quotes correctly:"
print"test \'many diffent\' \"types of \" quotes, even with \' \" trailing quotes"
print"""

Multi line quotes are great too!

"""
a=""" ::
one more multi-line quote won't hurt
"""
print a
Neil
źródło