Dus*_*etz 4 python monads functional-programming
我有一个函数m_chain,它引用了两个函数bind,unit但没有定义.我想在一些提供这些函数定义的上下文中包装此函数 - 您可以将它们视为我想动态提供实现的接口.
def m_chain(*fns):
"""what this function does is not relevant to the question"""
def m_chain_link(chain_expr, step):
return lambda v: bind(chain_expr(v), step)
return reduce(m_chain_link, fns, unit)
Run Code Online (Sandbox Code Playgroud)
在Clojure中,这是用宏完成的.在python中执行此操作的一些优雅方法是什么?我考虑过:
self.bind并且self.unit其实现由子类提供with界面,以便我可以修改环境贴图,然后在完成后清理理想情况下,我根本不想修改m_chain,我想按原样使用定义,并且所有上述选项都需要更改定义.这有点重要,因为还有其他m_*函数引用了在运行时提供的附加函数.
我如何最好地构建这个,以便我可以很好地传递bind和unit的实现?尽管实现复杂,但m_chain的最终用法非常容易使用,这一点非常重要.
编辑:这是另一种有效的方法,这很丑陋,因为它需要m_chain被cur成一个没有args的函数.但这是一个最低限度的工作示例.
def domonad(monad, cmf):
bind = monad['bind']; unit = monad['unit']
return cmf()
identity_m = {
'bind':lambda v,f:f(v),
'unit':lambda v:v
}
maybe_m = {
'bind':lambda v,f:f(v) if v else None,
'unit':lambda v:v
}
>>> domonad(identity_m, lambda: m_chain(lambda x: 2*x, lambda x:2*x)(2))
8
>>> domonad(maybe_m, lambda: m_chain(lambda x: None, lambda x:2*x)(2))
None
Run Code Online (Sandbox Code Playgroud)
在Python中,您可以编写所需的所有代码,这些代码指的是不存在的东西; 具体而言,您可以编写引用没有绑定值的名称的代码.你可以编译该代码.唯一的问题是在运行时发生,如果名称仍然没有绑定到它们的值.
这是一个可以运行的代码示例,在Python 2和Python 3下进行了测试.
def my_func(a, b):
return foo(a) + bar(b)
try:
my_func(1, 2)
except NameError:
print("didn't work") # name "foo" not bound
# bind name "foo" as a function
def foo(a):
return a**2
# bind name "bar" as a function
def bar(b):
return b * 3
print(my_func(1, 2)) # prints 7
Run Code Online (Sandbox Code Playgroud)
如果您不希望名称只是绑定在本地名称空间中,但您希望能够根据函数对它们进行微调,我认为Python中的最佳实践是使用命名参数.你总是可以关闭函数参数并返回一个新的函数对象,如下所示:
def my_func_factory(foo, bar):
def my_func(a, b):
return foo(a) + bar(b)
return my_func
my_func0 = my_func_factory(lambda x: 2*x, lambda x:2*x)
print(my_func0(1, 2)) # prints 6
Run Code Online (Sandbox Code Playgroud)
编辑:这是你的例子,使用上述想法进行修改.
def domonad(monad, *cmf):
def m_chain(fns, bind=monad['bind'], unit=monad['unit']):
"""what this function does is not relevant to the question"""
def m_chain_link(chain_expr, step):
return lambda v: bind(chain_expr(v), step)
return reduce(m_chain_link, fns, unit)
return m_chain(cmf)
identity_m = {
'bind':lambda v,f:f(v),
'unit':lambda v:v
}
maybe_m = {
'bind':lambda v,f:f(v) if v else None,
'unit':lambda v:v
}
print(domonad(identity_m, lambda x: 2*x, lambda x:2*x)(2)) # prints 8
print(domonad(maybe_m, lambda x: None, lambda x:2*x)(2)) # prints None
Run Code Online (Sandbox Code Playgroud)
请告诉我这对你有用.
编辑:好的,你的评论后还有一个版本.您可以m_按照此模式编写任意函数:它们检查kwargs密钥"monad".必须将其设置为命名参数; 没有办法将它作为位置参数传递,因为将*fns所有参数收集到列表中的参数.我所提供的缺省值bind()和unit()在它们没有在单子所定义,或不提供单子情况; 那些可能不会做你想要的,所以用更好的东西替换它们.
def m_chain(*fns, **kwargs):
"""what this function does is not relevant to the question"""
def bind(v, f): # default bind if not in monad
return f(v),
def unit(v): # default unit if not in monad
return v
if "monad" in kwargs:
monad = kwargs["monad"]
bind = monad.get("bind", bind)
unit = monad.get("unit", unit)
def m_chain_link(chain_expr, step):
return lambda v: bind(chain_expr(v), step)
return reduce(m_chain_link, fns, unit)
def domonad(fn, *fns, **kwargs):
return fn(*fns, **kwargs)
identity_m = {
'bind':lambda v,f:f(v),
'unit':lambda v:v
}
maybe_m = {
'bind':lambda v,f:f(v) if v else None,
'unit':lambda v:v
}
print(domonad(m_chain, lambda x: 2*x, lambda x:2*x, monad=identity_m)(2))
print(domonad(m_chain, lambda x: None, lambda x:2*x, monad=maybe_m)(2))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2298 次 |
| 最近记录: |