什么是组合器,它们如何应用于编程项目?(实用说明)


51

什么是组合器?

我在找:

  • 实际的解释
  • 如何使用它们的示例
  • 组合器如何提高代码质量/通用性的示例

我不是在寻找:

  • 不能帮助我完成工作的组合器的说明(例如Y组合器)

组合器类似于“副词”,它们吸收函数然后返回其他函数。它们可以帮助消除重复的代码,因为您不需要在变量之间插入。一些有用的是两次(f)= \ x-> f(f(x)),flip(op)-> \ xy-> y op x,(。)如(fg)x = f(g(x )),($)可以帮助映射(在infix中称为<$>),如($ 5)<$> [(+1),(* 2)] = [6,10],可在Lisp中使用curry / Python / JavaScript用于部分应用程序,并且uncurry可用于需要在Haskell中进行记录(元组)的函数。当x |> f = fa时,x |>(长度&&&&和)|> uncurry(/)是平均值。
aoeu256

Answers:


51

从实用的角度来看,组合器是一种编程构造,可让您以有趣且通常为高级的方式组合逻辑。通常,使用它们取决于能否将可执行代码打包到对象中(通常出于历史原因)称为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组合器就可以用任何现代语言轻松创建它们。


9

将Y-combinator品牌化是错误的,因为它不会“帮助完成工作”。我发现它在许多场合下都非常有用。最明显的情况是您必须快速引导一些嵌入式解释语言。如果你提供一套最小的原语,即sequenceselectcallconstclosure allocation,它是建立一个完整的,任意复杂的语言已经足够了。不需要特殊的递归支持,可以通过定点组合器添加。否则,您将需要更复杂的原语。

组合器的另一个明显情况是混淆。转换为SKI演算的代码实际上是不可读的。如果您确实需要混淆算法的实现,请考虑使用组合器,这是一个示例

而且,当然,组合器是实现功能语言的重要工具。最简单的方法(如上例所示)是通过SKI或等效演算。超级组合器用于其他一些实现中。这本书深入地讨论了它。

这是一个玩笑,但值得一读,因为很多奥术编程技术和理论都在那儿讲了。


1
@MattFenwick,经常会出现您无法期望的简单的嵌入式解释器需求。例如,就我而言,这是我必须设计的一种语言,以扩展通信协议。简单的IPC是不够的,因此该协议必须是可执行的。
SK-logic

@MattFenwick,关于您的问题:您可以尝试用APL或J编写一些代码。组合器在这里很重要,因此您将了解如何正确应用它们。另外,阅读无点样式可能会有所帮助:en.wikipedia.org/wiki/Tacit_programming
SK-logic

7

仔细研究一下,我发现了一个StackOverflow问题,“组合器”的很好解释(对于非数学家),恰好是该问题的表亲。 答案之一是指向Reginald Braithwaite的博客Homoiconic的链接,该博客链接了代码中几个有用的组合器示例(例如,由Ruby的方法实现的K组合器Object#tap -请阅读该页面以获取其有用性的示例)。

组合逻辑上Wikipedia页面更全面地描述了组合器。


这解决了我的问题的第二点。谢谢你的例子!

1
这篇文章有一些很好的链接,但实际上并没有直接回答问题。答案应自己完成,并在必要时使用链接作为参考。
Aaronaught
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.