Schreiben wir einen Minifier

14

Hintergrund

Minifier werden in der Regel verwendet, wenn Sie JavaScript in Ihrem Webbrowser bereitstellen. Es wird häufig verwendet, um die Anzahl der zu sendenden Bytes zu verringern. Das Sparen von Bandbreite ist aus offensichtlichen Gründen nützlich. Einige Leute benutzen Obfuscater (die absichtlich das Lesen von Code erschweren), ich spreche nicht über diese.

Wir werden Python 2 minimieren

Ich habe überlegt, ob JavaScript oder Python für die Minimierungserfahrung verwendet werden soll oder nicht, und ich habe mich aus zwei Gründen für Python entschieden: Auf den Leerraum kommt es an, und ich denke, das wird dem Problem eine interessante Dynamik verleihen. Darüber hinaus bietet die Verwendung von Python 2.7 eine weitere Dynamik, z. B. das Entfernen überflüssiger Elemente ()während eines Druckvorgangs (z. B. print("Hello world")vs. print"Hello world"). Ich persönlich hätte es vorgezogen, es für jede Sprache zu öffnen, aber für einige Sprachen ist dieser Prozess nicht sehr sinnvoll. Und welche Sprache Sie sich für die Minimierung entscheiden, wirkt sich direkt auf Ihre Punktzahl aus (und ob die Sprache überhaupt minimiert werden kann).

Technische Daten

Ihr Ziel ist es, den Code nur so zu ändern, dass seine Funktionalität in keiner Weise verändert wird. Sie können sich natürlich ändern Variablennamen (in Ihrem Verkleinerungsprogramm), solange sie nicht wirksam ausgegeben hat ( im Auge behalten Umfang ). Obwohl ich Ihnen ein spezifisches Programm gebe, optimieren Sie es bitte nicht für den Testfall, da alle Standardlücken verboten sind.

Ergebnis : Länge des Programms, nachdem Sie es minimiert haben.

Eingabe : Beliebiges Python 2.7-Programm (das keine Fehler enthält)

Ausgabe : Eine verkleinerte Version.

Obwohl Ihr Code in der Lage sein sollte, alle gültigen Python 2.7-Eingaben aufzunehmen, ist es erforderlich, Ihr Skript mit etwas zu testen, um seine Wirksamkeit zu beweisen.

Klicken Sie hier , um das Beispielprogramm anzuzeigen.

Das Problem verständlicher machen

Fühlen Sie sich frei, Code zu verwenden oder zu ändern, der sich in meiner Lösung befindet (siehe unten). Ich habe dies getan, um Ihnen den Einstieg in die grundlegende Angebotsabwicklung zu erleichtern. Sie können es jedoch auf Einrückung usw. erweitern.

Beispiel für die Minimierung von Python

Der gesamte Leerraum könnte mit der minimal möglichen Menge ersetzt werden (ich gebe zu, dass Sie in Python einige knifflige Dinge mit Tabulatoren machen können , aber das überlasse ich Ihnen, um zu entscheiden, ob Sie es implementieren oder nicht).

Beispiel

Folgende:

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

Könnte sein:

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

Technisch gesehen können Sie eine Schleife, die nur eine Zeile enthält, noch weiter komprimieren:

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

Es gibt jedoch eine andere Möglichkeit, Leerzeichen in Python zu minimieren:

Folgende:

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

Könnte sein:

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

Beachten Sie, dass kein Leerzeichen zwischen 2und erforderlich ist for. Variablen, Funktionen und Schlüsselwörter dürfen nicht mit einer Zahl beginnen. Der Python-Interpreter ist also in Ordnung <num><keyword>, kein Leerzeichen. Beachten Sie auch, dass zwischen )und kein Leerzeichen stehen muss if.

Beachten Sie, dass Sie die Ausgabe des Programms nicht ändern dürfen! So:

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

Die obige print-Anweisung sollte gleich bleiben, da das Leerzeichen dazwischen entfernt 2und isdie Ausgabe geändert würde.

Neil
quelle
Nebenbemerkung: Es gibt kein Programm, das das kürzeste ausgeben kann Äquivalent eines beliebigen Eingabeprogramms gemäß dieser Diskussion
Leaky Nun
Es gibt einige Python-Minifier-Tools bereits . Ich denke nicht, dass diese Frage eine bessere Lösung als die bereits vorhandenen Tools erhalten könnte.
TSH
Verändert sich '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'in '1'*100erlaubt? Müssen Sie tun, wie das Verhalten gleich ist?
14.

Antworten:

2

Python 2.7, 2013 Punktzahl

Dieses Programm kann als Referenz verwendet werden. Sie können den folgenden Code verwenden, ändern und dann in Ihren eigenen Lösungen veröffentlichen.

Im Nachhinein hätte ich vielleicht auch Regex für die Angebotsabwicklung verwenden sollen, aber ich denke, dass es im aktuellen Zustand ausreichen könnte, um die Leute für das Problem zu begeistern.

Warum ich mich für Python 2.7 entschieden habe: Ich dachte, es wäre einfacher zu testen, ob ich das Programm über das execSchlüsselwort zum Absturz gebracht habe .

Dieser Code nimmt das Programm als auf in.txt.

Ich dachte mir, ich sollte zumindest den Ball ins Rollen bringen für alle, die teilnehmen möchten, indem ich einen Zitat-Parser (der auch Kommentare verarbeitet) und ein kurzes Beispiel schreibe, wie Regex in Kombination mit dem Zitat-Parser das Spiel wirklich verändern kann Komplexität dieses Problems.

Hinweis: Bei diesem Minifier gibt es noch viel Raum für Verbesserungen. Als ob Sie mit Einrückungen, Variablennamen und dem Entfernen der Klammern herumspielen könnten, wenn sie mit meinen Schlüsselwörtern wie printoder verwendet werden 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)

Ausgabe des Programms:

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
quelle