为什么我不会在大多数高级语言中看到管道运营商?

时间:2009-10-19 03:39:31

标签: language-agnostic programming-languages concurrency shell pipe

在Unix shell编程中,pipe operator是一个非常强大的工具。使用一小组核心实用程序,系统语言(如C)和脚本语言(如Python),您可以构建极其紧凑且功能强大的shell脚本,这些脚本由操作系统自动并行化。

显然这是一个非常强大的编程范例,但我没有将管道视为除shell脚本之外的任何语言的一流抽象。使用管道复制脚本功能所需的代码似乎总是非常复杂。

所以我的问题是为什么我在C#,Java等现代高级语言中看不到类似于Unix管道的东西?是否有支持一流管道的语言(除了shell脚本)?表达并发算法不是一种方便安全的方法吗?

为了防止有人提起它,我查看了F#管道转发操作符(转发管道操作符),它看起来更像是一个函数应用程序操作符。它将函数应用于数据,而不是将两个流连接在一起,据我所知,但我愿意进行更正。

Postscript :在进行协同程序的一些研究时,我意识到有一些相似之处。在一个blog post马丁沃尔夫描述了一个与我相似的问题,但在协程而不是管道方面。

15 个答案:

答案 0 :(得分:10)

哈哈!感谢我的Google-fu,我找到了您感兴趣的an SO answer。基本上,答案是反对“不要重载运算符,除非你真的必须”通过重载按位运算符来提供类似shell的管道的参数,导致像这样的Python代码:

for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7):
    print i

从概念上讲,它的作用是将数字列表从2到99(xrange(2, 100))通过筛选函数传递,该函数删除给定数字的倍数(前2个,然后3个,然后是5个,然后是7个) 。这是素数生成器的开始,虽然以这种方式生成素数是一个相当糟糕的想法。但我们可以做得更多:

for i in xrange(2,100) | strify() | startswith(5):
    print i

这会生成范围,然后将所有数据从数字转换为字符串,然后过滤掉任何不以5开头的内容。

该帖子显示了一个基本的父类,它允许您重载两个方法mapfilter,以描述管道的行为。因此strify()使用map方法将所有内容转换为字符串,而sieve()使用filter方法清除不是数字倍数的内容。

它非常聪明,但也许这意味着它不是非常Pythonic,但它展示了你所追求的东西以及一种可以轻松应用于其他语言的技术。

答案 1 :(得分:6)

你可以在Erlang中轻松地进行流水线类型并行化。以下是我2008年1月的博文中的无耻复制/粘贴。

此外,Glasgow Parallel Haskell允许并行功能组合,这相当于同样的事情,为您提供隐式并行化。

  

你已经考虑过了   管道 - “gzcat怎么样?   foo.tar.gz | tar xf - “?你可能不会   知道它,但外壳是   运行解压缩并解压缩   parallel - stdin只读取stdin   阻止数据发送到stdout   gzcat。

     

很多任务都可以表达出来   在管道方面,如果可以的话   那样做,然后达到一定程度   David的并行化很简单   King的助手代码(甚至跨越erlang   节点,即。机器):

pipeline:run([pipeline:generator(BigList),
          {filter,fun some_filter/1},
          {map,fun_some_map/1},
          {generic,fun some_complex_function/2},
          fun some_more_complicated_function/1,
          fun pipeline:collect/1]).
     

所以基本上他在这里做的是   制作步骤列表 - 每一步   正在实现一个有趣的   接受以前的输入   步输出(甚至可以   当然定义内联)。去检查   大卫的blog entry   代码和更详细的解释。

答案 2 :(得分:5)

您可以在C#和Java中找到类似管道的内容,例如,您将连接流放入另一个连接流的构造函数中。

所以,你有Java:

new BufferedReader(new InputStreamReader(System.in));

您可能希望查找链接输入流或输出流。

答案 3 :(得分:4)

magrittr package在R中提供类似于F#的管道转发运算符:

rnorm(100) %>% abs %>% mean

结合dplyr包,它带来了一个整洁的数据处理工具:

iris %>%
  filter(Species == "virginica") %>%
  select(-Species) %>%
  colMeans

答案 4 :(得分:2)

通常你不需要它,如果没有它,程序运行得更快。

基本上,管道是消费者/生产者模式。写这些消费者和生产者并不难,因为他们没有太多的数据。

  • 用于Python的管道:pypes
  • Mozart-OZ可以使用端口和线程来管道。

答案 5 :(得分:2)

感谢所有出色的答案和评论,以下是我学到的内容摘要:

事实证明,有一个完整的范例与我感兴趣的名为Flow-based programming的内容有关。专门为基于流程的编程设计的语言的一个很好的例子是Hartmann pipelines。 Hartamnn管道概括了Unix和其他操作系统中使用的流和管道的概念,允许多个输入和输出流(而不仅仅是单个输入流和两个输出流)。 Erlang包含强大的抽象,可以很容易地以类似于管道的方式表达并发进程。 Java提供了PipedInputStreamPipedOutputStream,它们可以与线程一起使用,以更详细的方式实现相同类型的抽象。

答案 6 :(得分:2)

你在看F#|>运营商?我想你其实想要>>操作

答案 7 :(得分:1)

我认为最根本的原因是因为C#和Java倾向于用于构建更多的单片系统。在文化方面,即使想要做类似管道的东西也是不常见的 - 你只需要让你的应用程序实现必要的功能。构建大量简单工具然后以任意方式将它们粘合在一起的概念在这些情况下并不常见。

如果你看一些脚本语言,比如Python和Ruby,有一些非常好的工具可以在这些脚本中做类似管道的事情。例如,查看Python子进程模块,它允许您执行以下操作:

proc = subprocess.Popen('cat -',
                       shell=True,
                       stdin=subprocess.PIPE,
                       stdout=subprocess.PIPE,)
stdout_value = proc.communicate('through stdin to stdout')[0]
print '\tpass through:', stdout_value

答案 8 :(得分:1)

Objective-C有NSPipe类。我经常使用它。

答案 9 :(得分:1)

我在Python中构建管道功能有很多乐趣。我有一个我写的库,我把内容和样本运行here。最适合我的是XML处理,如Wikipedia article中所述。

答案 10 :(得分:1)

基于协同程序的流式库已经存在于Haskell已有一段时间了。两个流行的示例是conduitpipes

这两个库都写得很好并且记录完备,并且相对成熟。 Yesod Web框架基于管道,它是pretty damn fast。 Yesod在性能上与Node竞争,甚至在一些地方击败它。

有趣的是,默认情况下,所有这些库都是单线程的。这是因为管道的单一激励用例是服务器,它们是I / O绑定的。

答案 11 :(得分:0)

您可以通过链接/过滤/转换迭代器来管理Java中的操作。 您可以使用Google的Guava Iterators

我会说,即使使用非常有用的guava库和静态导入,它仍然会有很多Java代码。

在Scala中,很容易制作自己的管道操作员。

答案 12 :(得分:0)

如果你仍然对答案感兴趣......

你可以看一下因素,或者更老的欢乐和结合范式。 在arguments和out参数中是隐式的,转储到堆栈中。然后下一个单词(函数)获取该数据并对其执行某些操作。

语法是后缀。

“123”打印

其中print接受一个参数,无论堆栈中是什么。

答案 13 :(得分:0)

您可以在python中使用我的库:github.com/sspipe/sspipe

答案 14 :(得分:0)

自从R今天增加了管道运算符以来,值得一提的是Julialang拥有管道的时间很长:

help?> |>
search: |>

  |>(x, f)


  Applies a function to the preceding argument. This allows for easy function chaining.

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> [1:5;] |> x->x.^2 |> sum |> inv
  0.01818181818181818