Python中的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))
现在的问题是:这样的组合可以写在 pointfree 风格,如:
filter(is_divisible_by_13 and is_palindrome, range(1000,10000))
这当然达不到预期的效果,因为 lambda 函数的真值是 True
和 and
和 或
是短路运算符。我想到的最接近的事情是定义一个类 P
,它是一个简单的谓词容器,它实现 __call__()
并具有方法 and_() 和
or_()
来组合谓词。 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 函数。这更接近我想要的,但它也不是无点的(点现在是谓词本身而不是它们的参数)。现在第二个问题是:是否有比使用 P 等类且不使用 (lambda) 函数更好或更短的方法(可能没有括号和点)在 Python 中组合谓词?
I have some predicates, e.g.:
is_divisible_by_13 = lambda i: i % 13 == 0
is_palindrome = lambda x: str(x) == str(x)[::-1]
and want to logically combine them as in:
filter(lambda x: is_divisible_by_13(x) and is_palindrome(x), range(1000,10000))
The question is now: Can such combination be written in a pointfree style, such as:
filter(is_divisible_by_13 and is_palindrome, range(1000,10000))
This has of course not the desired effect because the truth value of lambda functions is True
and and
and or
are short-circuiting operators. The closest thing I came up with was to define a class P
which is a simple predicate container that implements __call__()
and has the methods and_()
and or_()
to combine predicates. The definition of P
is as follows:
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
With P
I can now create a new predicate that is a combination of predicates like this:
P(is_divisible_by_13).and_(is_palindrome)
which is equivalent to the above lambda function. This comes closer to what I'd like to have, but it is also not pointfree (the points are now the predicates itself instead of their arguments). Now the second question is: Is there a better or shorter way (maybe without parentheses and dots) to combine predicates in Python than using classes like P
and without using (lambda) functions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您可以通过向
P
类添加__and__
方法来重写 Python 中的&
(按位 AND)运算符。然后,您可以编写如下内容:甚至
类似地,您可以通过添加
__or__
方法和~
来覆盖|
(按位 OR)运算符(按位求反)运算符,通过添加 __not__ 方法。请注意,您无法覆盖内置的and
、or
和not
运算符,因此这可能尽可能接近您的目标。您仍然需要有一个P
实例作为最左边的参数。为了完整起见,您还可以覆盖就地变体(
__iand__
、__ior__
)和右侧变体(__rand__
、< code>__ror__) 这些运算符。代码示例(未经测试,请随意更正):
让您更接近无点必杀技的另一个技巧是以下装饰器:
然后您可以使用
predicate
装饰器标记您的谓词以使它们成为一个实例自动的P
:You can override the
&
(bitwise AND) operator in Python by adding an__and__
method to theP
class. You could then write something like:or even
Similarly, you can override the
|
(bitwise OR) operator by adding an__or__
method and the~
(bitwise negation) operator by adding a__not__
method. Note that you cannot override the built-inand
,or
andnot
operator, so this is probably as close to your goal as possible. You still need to have aP
instance as the leftmost argument.For sake of completeness, you may also override the in-place variants (
__iand__
,__ior__
) and the right-side variants (__rand__
,__ror__
) of these operators.Code example (untested, feel free to correct):
One more trick to bring you closer to point-free nirvana is the following decorator:
You can then tag your predicates with the
predicate
decorator to make them an instance ofP
automatically:基本上,你的方法似乎是 Python 中唯一可行的方法。 github 上有一个 python 模块,使用大致相同的机制来实现无点函数组合。
我没有使用过它,但乍一看他的解决方案看起来更好一些(因为他在使用类和 __call__ 的地方使用了装饰器和运算符重载)。
但除此之外,它在技术上不是无点代码,如果你愿意的话,它只是“点隐藏”。这对你来说可能足够也可能不够。
Basically, your approach seems to be the only feasible one in Python. There's a python module on github using roughly the same mechanism to implement point-free function composition.
I have not used it, but at a first glance his solution looks a bit nicer (because he uses decorators and operator overloading where you use a class and
__call__
).But other than that it's not technically point-free code, it's just "point-hidden" if you will. Which may or may not be enough for you.
您可以使用 中缀运算符配方
:
You could use the Infix operator recipe:
yields
Python 已经有一种组合两个函数的方法:lambda。您可以轻松制作自己的撰写和多个撰写功能:
Python already has a way of combining two functions: lambda. You can easily make your own compose and multiple compose functions:
这将是我的解决方案:
这是我的结果:
That would be my solution:
And this is my result:
这是一个基于 Ramda.js 库的解决方案,该库具有
allPass
、< code>anyPass 和complement
组合器专门用于此目的。以下是 Python 3.10 中实现的函数:Here's a solution based on the Ramda.js library which has the
allPass
,anyPass
, andcomplement
combinators especially for this purpose. Here are those functions implemented in Python 3.10: