什么是组合器?
我在找:
- 实际的解释
- 如何使用它们的示例
- 组合器如何提高代码质量/通用性的示例
我不是在寻找:
- 不能帮助我完成工作的组合器的说明(例如Y组合器)
什么是组合器?
我在找:
我不是在寻找:
Answers:
从实用的角度来看,组合器是一种编程构造,可让您以有趣且通常为高级的方式组合逻辑。通常,使用它们取决于能否将可执行代码打包到对象中(通常出于历史原因)称为lambda函数或lambda表达式的对象,但是您的想法会有所不同。
一个简单的(有用的)组合器示例是一个使用两个不带参数的lambda函数并创建一个按顺序运行它们的新函数的例子。实际的组合器看起来像这样的通用伪代码:
func in_sequence(first, second):
lambda ():
first()
second()
使之成为组合器的关键是第二行的匿名函数(lambda函数)。你打电话时
a = in_sequence(f, g)
生成的对象a 不是先运行f()然后运行g()的结果,而是一个可以稍后调用以依次执行f()和g()的对象:
a() // a is a callable object, i.e. a function without parameters
同样,您可以有一个并行运行两个代码块的组合器:
func in_parallel(first, second):
lambda ():
t1 = start_thread(first)
t2 = start_thread(second)
wait(t1)
wait(t2)
再说一次
a = in_parallel(f, g)
a()
很酷的事情是,“ in_parallel”和“ in_sequence”都是具有相同类型/签名的组合器,即它们都接受两个无参数的函数对象并返回一个新的对象。然后,您实际上可以编写类似
a = in_sequence(in_parallel(f, g), in_parallel(h, i))
并且它按预期工作。
基本上,组合器使您能够以程序化和灵活的方式构造程序的控制流(除其他外)。例如,如果使用in_parallel(..)组合器在程序中运行并行性,则可以将与之相关的调试添加到in_parallel组合器本身的实现中。以后,如果您怀疑程序中存在与并行性有关的错误,则实际上可以重新实现in_parallel:
in_parallel(first, second):
in_sequence(first, second)
一键将所有平行部分转换为连续部分!
正确使用组合器非常有用。
但是,现实生活中不需要Y组合器。它是一个组合器,可让您创建自递归函数,并且无需Y组合器就可以用任何现代语言轻松创建它们。
将Y-combinator品牌化是错误的,因为它不会“帮助完成工作”。我发现它在许多场合下都非常有用。最明显的情况是您必须快速引导一些嵌入式解释语言。如果你提供一套最小的原语,即sequence
,select
,call
,const
和closure allocation
,它是建立一个完整的,任意复杂的语言已经足够了。不需要特殊的递归支持,可以通过定点组合器添加。否则,您将需要更复杂的原语。
组合器的另一个明显情况是混淆。转换为SKI演算的代码实际上是不可读的。如果您确实需要混淆算法的实现,请考虑使用组合器,这是一个示例。
而且,当然,组合器是实现功能语言的重要工具。最简单的方法(如上例所示)是通过SKI或等效演算。超级组合器用于其他一些实现中。这本书深入地讨论了它。
这是一个玩笑,但值得一读,因为很多奥术编程技术和理论都在那儿讲了。
仔细研究一下,我发现了一个StackOverflow问题,“组合器”的很好解释(对于非数学家),恰好是该问题的表亲。 答案之一是指向Reginald Braithwaite的博客Homoiconic的链接,该博客链接了代码中几个有用的组合器示例(例如,由Ruby的方法实现的K组合器Object#tap
-请阅读该页面以获取其有用性的示例)。
组合逻辑上的Wikipedia页面更全面地描述了组合器。