Python中的无点函数组合
我有一些谓词,例如:Python中的无点函数组合,python,predicate,function-composition,pointfree,Python,Predicate,Function Composition,Pointfree,我有一些谓词,例如: is_divisible_by_13 = lambda i: i % 13 == 0 is_palindrome = lambda x: str(x) == str(x)[::-1] 并希望以逻辑方式将它们组合在一起,如下所示: filter(lambda x: is_divisible_by_13(x) and is_palindrome(x), range(1000,10000)) 现在的问题是:这样的组合是否可以用一种风格来书写,比如: filter(is_div
is_divisible_by_13 = lambda i: i % 13 == 0
is_palindrome = lambda x: str(x) == str(x)[::-1]
并希望以逻辑方式将它们组合在一起,如下所示:
filter(lambda x: is_divisible_by_13(x) and is_palindrome(x), range(1000,10000))
现在的问题是:这样的组合是否可以用一种风格来书写,比如:
filter(is_divisible_by_13 and is_palindrome, range(1000,10000))
这当然没有期望的效果,因为lambda函数的真值是True
和以及和或是短路运算符。我想到的最近的一件事是定义一个类P
,它是一个简单的谓词容器,实现\uuuuuu调用
,并具有组合谓词的方法和或
。P
的定义如下:
import copy
class P(object):
def __init__(self, predicate):
self.pred = predicate
def __call__(self, obj):
return self.pred(obj)
def __copy_pred(self):
return copy.copy(self.pred)
def and_(self, predicate):
pred = self.__copy_pred()
self.pred = lambda x: pred(x) and predicate(x)
return self
def or_(self, predicate):
pred = self.__copy_pred()
self.pred = lambda x: pred(x) or predicate(x)
return self
使用p
我现在可以创建一个新的谓词,它是如下谓词的组合:
P(is_divisible_by_13).and_(is_palindrome)
这相当于上述lambda函数。这更接近于我想要的,但它也不是无点的(点现在是谓词本身,而不是它们的参数)。现在,第二个问题是:在Python中,有没有比使用类(如P
和不使用(lambda)函数更好或更短的方法(可能没有括号和点)来组合谓词?可以覆盖
和(按位和)通过向P
类添加\u和
方法,使用Python中的运算符。然后你可以写这样的东西:
P(is_divisible_by_13) & P(is_palindrome)
甚至
P(is_divisible_by_13) & is_palindrome
类似地,您可以通过添加\uuuu或
方法覆盖
(按位OR)运算符,并通过添加\uu非方法覆盖~
(按位求反)运算符。请注意,您无法覆盖内置的和
、或
和非
运算符,因此这可能会尽可能接近您的目标。您仍然需要一个P
实例作为最左边的参数
为完整起见,您还可以覆盖这些运算符的就地变量(\uu i和
,\uuu ior\uuuu
)和右侧变量(\uu rand\uuu
,\uu ror\uu
)
代码示例(未经测试,请随意更正):
让您更接近无点涅盘的另一个技巧是以下装饰器:
from functools import update_wrapper
def predicate(func):
"""Decorator that constructs a predicate (``P``) instance from
the given function."""
result = P(func)
update_wrapper(result, func)
return result
然后,您可以使用谓词
装饰器标记谓词,使其自动成为p
的实例:
@predicate
def is_divisible_by_13(number):
return number % 13 == 0
@predicate
def is_palindrome(number):
return str(number) == str(number)[::-1]
>>> pred = (is_divisible_by_13 & is_palindrome)
>>> print [x for x in xrange(1, 1000) if pred(x)]
[494, 585, 676, 767, 858, 949]
基本上,您的方法似乎是Python中唯一可行的方法。有一种方法使用大致相同的机制来实现无点函数组合
我没有使用过它,但乍一看他的解决方案看起来更好一些(因为他在使用类和\uu调用的地方使用了decorators和操作符重载)
但除此之外,它在技术上不是无点代码,如果你愿意,它只是“点隐藏”。这对您来说可能足够,也可能不够。您可以使用:
屈服
1001
2002
3003
4004
5005
6006
7007
8008
9009
这就是我的解决方案:
class Chainable(object):
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
return self.function(*args, **kwargs)
def __and__(self, other):
return Chainable( lambda *args, **kwargs:
self.function(*args, **kwargs)
and other(*args, **kwargs) )
def __or__(self, other):
return Chainable( lambda *args, **kwargs:
self.function(*args, **kwargs)
or other(*args, **kwargs) )
def is_divisible_by_13(x):
return x % 13 == 0
def is_palindrome(x):
return str(x) == str(x)[::-1]
filtered = filter( Chainable(is_divisible_by_13) & is_palindrome,
range(0, 100000) )
i = 0
for e in filtered:
print str(e).rjust(7),
if i % 10 == 9:
print
i += 1
这是我的结果:
0 494 585 676 767 858 949 1001 2002 3003
4004 5005 6006 7007 8008 9009 10101 11011 15951 16861
17771 18681 19591 20202 21112 22022 26962 27872 28782 29692
30303 31213 32123 33033 37973 38883 39793 40404 41314 42224
43134 44044 48984 49894 50505 51415 52325 53235 54145 55055
59995 60606 61516 62426 63336 64246 65156 66066 70707 71617
72527 73437 74347 75257 76167 77077 80808 81718 82628 83538
84448 85358 86268 87178 88088 90909 91819 92729 93639 94549
95459 96369 97279 98189 99099
Python已经有了一种结合两个函数的方法:lambda。您可以轻松创建自己的合成和多个合成函数:
compose2 = lambda f,g: lambda x: f(g(x))
compose = lambda *ff: reduce(ff,compose2)
filter(compose(is_divisible_by_13, is_palindrome, xrange(1000)))
似乎您正在非常努力地使非函数式语言表现得像函数式语言。你喜欢蟒蛇吗?@Eric:是的,有点。我的Python代码嵌入C++项目中,我不能只切换到另一种语言。我不确定是否想用另一种意义来重载位运算器,但我发现装饰器的想法非常有趣。谢谢@托马斯:按位运算对谓词有意义吗?如果不是的话,重载也没什么错。这可能是2012年Python年度最佳黑客。至少对我来说。@Tamás:这个食谱已经是2005年最好的黑客了:-)而且,它是费迪南德·贾米茨基写的,不是我。我只是(阿布?)用它来回答。它应该是reduce(compose2,ff)
,或者只是reduce(lambda f,g:lambda x:f(g(x)),ff)
0 494 585 676 767 858 949 1001 2002 3003
4004 5005 6006 7007 8008 9009 10101 11011 15951 16861
17771 18681 19591 20202 21112 22022 26962 27872 28782 29692
30303 31213 32123 33033 37973 38883 39793 40404 41314 42224
43134 44044 48984 49894 50505 51415 52325 53235 54145 55055
59995 60606 61516 62426 63336 64246 65156 66066 70707 71617
72527 73437 74347 75257 76167 77077 80808 81718 82628 83538
84448 85358 86268 87178 88088 90909 91819 92729 93639 94549
95459 96369 97279 98189 99099
compose2 = lambda f,g: lambda x: f(g(x))
compose = lambda *ff: reduce(ff,compose2)
filter(compose(is_divisible_by_13, is_palindrome, xrange(1000)))