让我们写一个Minifier


14

背景

通常,在向Web浏览器提供JavaScript时使用缩小符。通常用于减少必须发送的字节数。显而易见,节省带宽很有用。有些人使用混淆器(故意使代码难以阅读),我不是在谈论那些。

我们将缩小Python 2

我在讨论是否使用JavaScript或Python来获得最小化的体验,而我决定使用Python的原因有两个:空白很重要,我认为这会增加一个有趣的动态问题。此外,使用Python 2.7将提供另一种动态效果,例如()在打印过程中删除多余的内容(即print("Hello world")vs. print"Hello world")。就我个人而言,我更愿意将其开放给任何语言,但是对于某些语言,此过程没有多大意义。而且,您决定缩小的语言将直接影响您的分数(甚至可以缩小该语言)。

眼镜

您的目标是仅以不会改变其功能的方式修改代码。当然,您可以更改变量名称(在缩小程序内部),只要它不影响输出(保持作用域)即可。尽管我为您提供了一个特定的程序,但是请不要针对测试用例进行优化,因为所有标准漏洞都被禁止。

分数:程序缩小后的长度。

输入:任何Python 2.7程序(不包含错误)

输出:缩小版本。

尽管您的代码应该能够容纳所有有效的Python 2.7输入,但是有必要对脚本进行测试以证明其有效性。

单击此处查看示例程序。

使问题更加平易近人

随意使用或修改在我的解决方案中找到的任何代码(列在下面)。我这样做是为了让您开始使用报价基本报价处理。但是,您可以将其扩展为缩进等。

缩小Python的示例方法

所有空白都可以用尽可能少的空间替换(我承认,在Python中,您可以使用tabs做一些棘手的事情,但是我将由您自己决定是否实现它)。

下列:

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

可能:

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

从技术上讲,如果循环内只有一行,则可以进一步压缩它:

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

但是,还有另一种方法可以缩小Python中的空白:

下列:

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

可能:

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

请注意,2和之间不需要有空格for。变量,函数和关键字不能以数字开头。因此,Python解释器可以使用<num><keyword>,没有空格。您还应该注意,)和之间没有空格if

注意,您不得更改程序的输出!所以:

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

上述print语句应该保持不变,因为消除之间的空间2,并is会修改输出。



旁注:根据本次讨论,没有任何程序可以输出与任意输入程序最短的等效程序
Leaky Nun

有做一些蟒蛇minifier工具已经。我认为这个问题可能没有比已经存在的工具更好的解决方案。
tsh

正在改变'1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111''1'*100允许吗?要求做的一样吗?
l4m2

Answers:


2

Python 2.7,2013年得分

该程序可用作参考,您可以采用以下代码并进行修改,然后将其发布到自己的解决方案中。

事后看来,我也许也应该使用正则表达式来处理报价,但是我认为在当前状态下,足以使人们开始陷入问题。

为什么选择Python 2.7:我认为测试起来是否更容易通过exec关键字使程序崩溃。

此代码以形式接收程序in.txt

我认为我至少应该编写一个报价解析器(也可以处理评论),让想参与的人参与其中,并简要说明一下正则表达式与报价解析器结合可以如何真正改变游戏规则的例子。这个问题的复杂性。

注意:此缩小器仍有很大的改进空间。就像您可以使用缩进,变量名并在使用我的关键字(例如print或)时删除括号一样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)

程序输出:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.