使用lisp和haskell的一部分来增强python的性能。
unpythonic的Python项目详细描述
我们为python提供了缺少的特性,主要来自列表处理 传统,但也夹杂着一些哈斯凯利斯主义。我们特别强调 clear,pythonic语法。
我们还可以选择将python语言的扩展作为 设计用于协同工作的语法宏。每个宏添加一个 可以(主要)混合和匹配的正交功能块 和其他人一起。
设计考虑的是简单性、健壮性和最小的依赖性。 当前不需要;macropy可选,以启用语法宏。
没有宏,我们的功能包括尾部调用优化(tco)、tco'd fp样式的循环、call/ec、let&letrec、assign once、多表达式lambdas, 动态分配(也称为参数化,特殊变量),记忆化 (也适用于发电机和发电机)、电流、功能组合, 折叠和扫描(左和右),打开,缓慢的部分打开。 序列、pythonic lispy链表(cons)和 用于创建支持中缀数学的数学序列的紧凑语法。
我们的curry修改了python的缩减规则。它传递任何额外的参数 右边的through,在剩下的部分调用一个可调用的返回值 参数,以便我们可以:
mymap = lambda f: curry(foldr, composerc(cons, f), nil) myadd = lambda a, b: a + b assert curry(mymap, myadd, ll(1, 2, 3), ll(2, 4, 6)) == ll(3, 6, 9) with_n = lambda *args: (partial(f, n) for n, f in args) clip = lambda n1, n2: composel(*with_n((n1, drop), (n2, take))) assert tuple(curry(clip, 5, 10, range(20))) == tuple(range(5, 15))
如果安装了macropy,unpythonic.syntax将可用。它提供了 宏实际上扩展了python语言,添加了 要么复杂,要么不可能以其他方式提供(和/或使用)。
使用宏,我们添加了自动套现、自动尾调用优化 (tco)、按需调用(惰性函数)、连续性(python的call/cc), {TT4}$(宏扩展时间的拼接代码),词汇范围 let和do具有精简语法、隐式返回语句和 易于使用的带有局部变量的多表达式lambdas。
tco宏有一个相当广泛的表达式分析器,所以 and,or,a if p else b和do[]和^{tt11}的任何用法$ 执行尾部调用转换时会考虑宏。
延续系统是基于 连续传递样式(CPS),连续表示为闭包。 它还使用与tco宏相同的机器自动应用tco。 为了使运行时开销保持合理,将捕获continuation 仅当使用call_cc[]显式请求时。
宏示例:
# let, letseq (let*), letrec with no boilerplate a = let((x, 17), (y, 23))[ (x, y)] # alternate haskelly syntax a = let[((x, 21),(y, 17), (z, 4)) in x + y + z] a = let[x + y + z, where((x, 21), (y, 17), (z, 4))] # cond: multi-branch "if" expression answer = lambda x: cond[x == 2, "two", x == 3, "three", "something else"] assert answer(42) == "something else" # do: imperative code in any expression position y = do[local[x << 17], print(x), x << 23, x] assert y == 23 # autocurry like Haskell with curry: def add3(a, b, c): return a + b + c assert add3(1)(2)(3) == 6 # actually partial application so these work, too assert add3(1, 2)(3) == 6 assert add3(1)(2, 3) == 6 assert add3(1, 2, 3) == 6 mymap = lambda f: foldr(composerc(cons, f), nil) myadd = lambda a, b: a + b assert mymap(myadd, ll(1, 2, 3), ll(2, 4, 6)) == ll(3, 6, 9) # lazy functions (call-by-need) like Haskell with lazify: def f(a, b): return a def g(a, b): return f(2*a, 3*b) assert g(21, 1/0) == 42 # the 1/0 is never evaluated # automatic tail-call optimization (TCO) like Scheme, Racket with tco: assert letrec((evenp, lambda x: (x == 0) or oddp(x - 1)), (oddp, lambda x: (x != 0) and evenp(x - 1)))[ evenp(10000)] is True # lambdas with multiple expressions, local variables, and a name with multilambda, namedlambda: myadd = lambda x, y: [print("myadding", x, y), local[tmp << x + y], print("result is", tmp), tmp] assert myadd(2, 3) == 5 assert myadd.__name__ == "myadd" # implicit "return" in tail position, like Lisps with autoreturn: def f(): print("hi") "I'll just return this" assert f() == "I'll just return this" def g(x): if x == 1: "one" elif x == 2: "two" else: "something else" assert g(1) == "one" assert g(2) == "two" assert g(42) == "something else" # splice code at macro expansion time with let_syntax: with block(a) as twice: a a with block(x, y, z) as appendxyz: lst += [x, y, z] lst = [] twice(appendxyz(7, 8, 9)) assert lst == [7, 8, 9]*2 # lispy prefix syntax for function calls with prefix: (print, "hello world") # the LisThEll programming language with prefix, curry: mymap = lambda f: (foldr, (compose, cons, f), nil) double = lambda x: 2 * x (print, (mymap, double, (q, 1, 2, 3))) assert (mymap, double, (q, 1, 2, 3)) == ll(2, 4, 6) # the HasThon programming language with curry, lazify: def add2first(a, b, c): return a + b assert add2first(2)(3)(1/0) == 5 assert letrec[((c, 42), (d, 1/0), (e, 2*c)) in add2first(c)(e)(d)] == 126 # call/cc for Python with continuations: stack = [] def amb(lst, cc): # McCarthy's amb operator if not lst: return fail() first, *rest = tuple(lst) if rest: ourcc = cc stack.append(lambda: amb(rest, cc=ourcc)) return first def fail(): if stack: f = stack.pop() return f() def pythagorean_triples(maxn): z = call_cc[amb(range(1, maxn+1))] y = call_cc[amb(range(1, z+1))] x = call_cc[amb(range(1, y+1))] if x*x + y*y != z*z: return fail() return x, y, z x = pythagorean_triples(20) while x: print(x) x = fail() # if Python didn't already have generators, we could add them with call/cc: with continuations: @dlet((k, None)) # let-over-def decorator def g(): if k: return k() def my_yield(value, cc): k << cc # rebind the k in the @dlet env cc = identity # override current continuation return value # generator body call_cc[my_yield(1)] call_cc[my_yield(2)] call_cc[my_yield(3)] out = [] x = g() while x is not None: out.append(x) x = g() assert out == [1, 2, 3]
有关文档和完整示例,请参见项目的github主页, 以及各个特性的文档字符串。举更多的例子, 请参阅源发行版中包含的单元测试。