Escribamos un minificador

14

Antecedentes

Los minificadores se usan, comúnmente, cuando se sirve JavaScript en su navegador web. Se usa comúnmente para reducir la cantidad de bytes que deben enviarse. Ahorrar ancho de banda es útil por razones obvias. Algunas personas usan ofuscadores (que intencionalmente hacen que el código sea más difícil de leer), no estoy hablando de eso.

Estaremos minificando Python 2

Estaba debatiendo si usar JavaScript o Python para la experiencia de minificación y decidí usar Python por dos razones: el espacio en blanco es importante y creo que eso agregará una dinámica interesante al problema. Además, el uso de Python 2.7 proporcionará otra dinámica, como eliminar lo superfluo ()durante una impresión (es decir, print("Hello world")vs. print"Hello world"). Personalmente, hubiera preferido abrirlo a cualquier idioma, pero para algunos idiomas este proceso no tendrá mucho sentido. Y, qué idioma decide minificar afectará directamente su puntaje (y si el idioma puede ser minificado).

Especificaciones

Su objetivo es solo modificar el código de una manera que no cambie su funcionalidad de ninguna manera. Por supuesto, puede cambiar los nombres de las variables (dentro de su programa minificador) siempre que no afecte la salida (realice un seguimiento del alcance ). Aunque le estoy dando un programa específico, no optimice para el caso de prueba ya que todas las lagunas estándar están prohibidas.

Puntuación : duración del programa después de minificarlo.

Entrada : cualquier programa Python 2.7 (que no contenga errores)

Salida : una versión minificada.

Aunque su código debe poder acomodar todas las entradas válidas de Python 2.7, es necesario probar su script contra algo para demostrar su efectividad.

Haga clic aquí para ver el programa de ejemplo.

Hacer el problema más accesible

Siéntase libre de usar o modificar cualquier código que se encuentre dentro de mi solución (que figura a continuación). Hice esto para ayudarlo a comenzar con el manejo básico de cotizaciones; sin embargo, puede expandirlo a sangría, etc.

Ejemplos de formas de minificar Python

Todo el espacio en blanco podría reemplazarse con la cantidad mínima posible (reconozco que en Python puede hacer algunas cosas difíciles con pestañas , pero lo dejaré a usted para decidir si implementarlo o no).

Ejemplo

El seguimiento:

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

Podría ser:

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

Técnicamente, si solo hay una línea dentro de un bucle, puede comprimirla aún más:

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

Sin embargo, hay otra forma de minimizar el espacio en blanco en Python:

El seguimiento:

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

Podría ser:

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

Tenga en cuenta que no hay necesidad de un espacio entre 2y for. Las variables, funciones y palabras clave no pueden comenzar con un número. Entonces, el intérprete de Python está de acuerdo <num><keyword>, no hay espacio. También debe tener en cuenta que no tiene que haber un espacio entre )y if.

Tenga en cuenta que no debe cambiar la salida del programa. Entonces:

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

La declaración de impresión anterior debería permanecer igual porque eliminar el espacio entre 2y ismodificaría la salida.

Neil
fuente
Nota al margen: no hay ningún programa que pueda generar el equivalente más corto de cualquier programa de entrada arbitraria, según esta discusión
Leaky Nun
Ya existen algunas herramientas de Python Minifier . No creo que esta pregunta pueda recibir una mejor solución que las herramientas ya existentes.
tsh
¿Se está cambiando '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'a '1'*100permitido? ¿Requiere hacer ya que el comportamiento es el mismo?
l4m2

Respuestas:

2

Python 2.7, puntaje 2013

Este programa se puede utilizar como referencia, y se le permite tomar el siguiente código y modificarlo y luego publicarlo en sus propias soluciones.

En retrospectiva, tal vez también debería haber usado regex para el manejo de cotizaciones, pero creo que en su estado actual puede ser suficiente para impulsar a las personas al problema.

Por qué elegí Python 2.7: pensé que sería más fácil probar si hice que el programa se bloqueara con la execpalabra clave.

Este código toma el programa como in.txt.

Pensé que al menos debería poner en marcha a quien quiera participar escribiendo un analizador de citas (que también maneja los comentarios) y un breve ejemplo de cómo la expresión regular, cuando se combina con el analizador de citas, realmente puede cambiar el juego en términos de La complejidad de este problema.

Nota: todavía hay mucho margen de mejora en este minificador. Al igual que podría jugar con sangría, nombres de variables y eliminar el paréntesis cuando se usan mis palabras clave, como printo 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)

Salida del programa:

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
fuente