Écrivons un Minifier

14

Contexte

Les minificateurs sont couramment utilisés lors de la diffusion de JavaScript dans votre navigateur Web. Il est couramment utilisé pour réduire le nombre d'octets à envoyer. La sauvegarde de la bande passante est utile pour des raisons évidentes. Certaines personnes utilisent des obscurcisseurs (qui rendent intentionnellement le code plus difficile à lire), je ne parle pas de ceux-ci.

Nous allons réduire Python 2

Je débattais sur l'utilisation ou non de JavaScript ou de Python pour l'expérience de minification et j'ai choisi Python pour deux raisons: les espaces blancs sont importants et je pense que cela ajoutera une dynamique intéressante au problème. De plus, l'utilisation de Python 2.7 fournira une autre dynamique, telle que la suppression superflue ()lors d'une impression (c.-à-d. print("Hello world")Vs print"Hello world"). J'aurais personnellement préféré l'ouvrir à n'importe quelle langue, mais pour certaines langues, ce processus n'aurait pas beaucoup de sens. Et, quelle langue vous décidez de minimiser aura un impact direct sur votre score (et si la langue peut même être minimisée).

Spécifications

Votre objectif est de modifier uniquement le code d'une manière qui ne changera en rien sa fonctionnalité. Vous pouvez, bien sûr, changer les noms de variables (dans votre programme de minification) tant que cela n'affecte pas la sortie (gardez une trace de la portée ). Bien que je vous donne un programme spécifique, veuillez ne pas optimiser le scénario de test car toutes les failles standard sont interdites.

Score : durée du programme après l'avoir minimisé.

Entrée : tout programme Python 2.7 (qui ne contient aucune erreur)

Sortie : une version réduite.

Bien que votre code devrait pouvoir accueillir toutes les entrées Python 2.7 valides, il est nécessaire de tester votre script par rapport à quelque chose afin de prouver son efficacité.

Cliquez ici pour voir l'exemple de programme.

Rendre le problème plus accessible

N'hésitez pas à utiliser ou à modifier tout code trouvé dans ma solution (listé ci-dessous). J'ai fait cela pour vous aider à démarrer avec la gestion de base des devis; cependant, vous pouvez l'étendre à l'indentation, etc.

Exemples de façons de réduire Python

Tous les espaces blancs pourraient être remplacés par le minimum possible (je reconnais qu'en Python, vous pouvez faire des choses délicates avec des onglets , mais je vous laisse le soin de décider de l'implémenter ou non).

Exemple

Le suivant:

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

Pourrait être:

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

Techniquement, s'il n'y a qu'une seule ligne dans une boucle, vous pouvez la compresser encore plus:

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

Cependant, il existe une autre façon de réduire l'espace blanc en Python:

Le suivant:

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

Pourrait être:

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

Notez qu'il n'y a pas besoin d'espace entre 2et for. La variable, les fonctions et les mots clés ne peuvent pas commencer par un nombre. Ainsi, l'interpréteur Python est d'accord avec <num><keyword>, pas d'espace. Vous devez également noter qu'il n'y a pas d'espace entre )et if.

Attention, vous ne devez pas modifier la sortie du programme! Donc:

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

L'instruction print ci-dessus doit rester la même car la suppression de l'espace entre 2et ismodifierait la sortie.

Neil
la source
Sidenote: il n'y a pas de programme capable de produire l' équivalent le plus court de tout programme d'entrée arbitraire, selon cette discussion
Leaky Nun
Il existe déjà des outils de minifieur python . Je ne pense pas que cette question puisse recevoir une meilleure solution que les outils déjà existants.
tsh
Le changement '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'en est-il '1'*100autorisé? Vous devez faire comme le comportement est le même?
l4m2

Réponses:

2

Score Python 2.7, 2013

Ce programme peut être utilisé comme référence, et vous êtes autorisé à prendre le code suivant et à le modifier, puis à le publier dans vos propres solutions.

Avec le recul, j'aurais peut-être dû aussi utiliser l'expression régulière pour la gestion des devis, mais je pense que dans son état actuel, cela pourrait suffire à faire démarrer le problème.

Pourquoi j'ai choisi Python 2.7: J'ai pensé qu'il serait plus facile de tester pour voir si j'ai fait planter le programme via le execmot - clé.

Ce code accepte le programme en tant que in.txt.

J'ai pensé que je devrais au moins lancer la balle pour quiconque veut participer en écrivant un analyseur de devis (qui arrive également à gérer les commentaires) et un bref exemple sur la façon dont regex, lorsqu'il est combiné avec l'analyseur de devis, peut vraiment changer le jeu en termes de complexité de ce problème.

Remarque: il y a encore beaucoup de place à l'amélioration dans cette minifieuse. Comme vous pourriez jouer avec l'indentation, les noms de variables et la suppression des parenthèses lorsqu'ils sont utilisés avec mes mots clés, comme printou 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)

Sortie du programme:

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
la source