为什么我在大多数高级语言中看不到pipe道操作员?

在Unix shell编程中, pipe道操作员是一个非常强大的工具。 通过一小组核心实用程序,一个系统语言(如C)和一个脚本语言(如Python),可以构build极其紧凑且function强大的shell脚本,并由操作系统自动并行化。

显然这是一个非常强大的编程范例,但是我还没有看到pipe道是除shell脚本以外的任何语言的一级抽象。 使用pipe道复制脚本function所需的代码似乎总是相当复杂。

所以我的问题是为什么我没有看到像C#,Java等现代高级语言中类似Unixpipe道的东西? 是否有支持一streampipe道的语言(不是shell脚本)? 这不是expression并发algorithm的一种方便而安全的方法吗?

为了防止有人出现,我查看了F#pipe道转发操作符(前向pipe道操作符),它看起来更像是一个函数应用操作符。 它将一个函数应用于数据,而不是将两个stream连接在一起,据我所知,但我愿意纠正。

后记 :在做协同工作的一些研究时,我意识到有一些相似之处。 在一篇博客文章中,马丁·沃尔夫描述了一个与我的类似的问题,但是在协程而不是pipe道方面。

你可以很容易地在Erlang中做stream水线types的并行。 以下是2008年1月份我的博客post的无耻复制/粘贴。

此外, 格拉斯哥并行Haskell允许并行function组成,这相当于同样的事情,给你隐式并行化。

你已经想到了pipe道 – “gzcat foo.tar.gz | tar xf – ”怎么样? 你可能不知道它,但shell正在运行解压缩并解压缩 – 在tar中读取的stdin只是阻塞,直到数据被gzcat发送到stdout。

那么很多任务可以用stream水线来表示,如果你可以这样做的话,那么通过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]). 

所以基本上他在这里做的是列出一个步骤 – 每个步骤都是以一个有趣的方式实现的,无论前面的步骤输出如何(甚至可以定义内联)。 去检查大卫的博客条目的代码和更详细的解释。

哈哈! 感谢我的Google-fu,我发现了一个可能令你感兴趣的SO答案 。 基本上,答案是违背了“不要重载操作符,除非你真的必须通过重载按位OR运算符来提供类似shell的pipe道,导致Python代码是这样的:

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

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

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

这将生成范围,然后将其全部从数字转换为string,然后过滤掉任何不以5开头的内容。

这篇文章显示了一个基本的父类,允许你重载两个方法, mapfilter来描述pipe道的行为。 所以strify()使用map方法将所有东西都转换成一个string,而sieve()使用filter方法filter那些不是数字倍数的东西。

这很聪明,虽然也许这意味着它不是Pythonic,但是它展现了你所追求的东西,以及一种能够很容易地将其应用于其他语言的技术。

你可以在C#和Java中find类似pipe道的东西,例如,你可以在其中获取连接stream并将其放入另一个连接stream的构造器中。

所以,你有在Java:

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

您可能想要查找链接inputstream或输出stream。

magrittr 包提供了一些类似于F中的R的pipe道转发操作符:

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

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

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

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

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

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

感谢所有伟大的回答和评论,这里是我学到的东西的总结:

事实certificate,我所感兴趣的stream程编程有一个完整的范例。 Hartmannstream水线是专门为基于stream程编程devise的语言的一个很好的例子。 Hartamnnstream水线概括了Unix和其他操作系统中使用的stream和pipe道的思想,允许多个input和输出stream(而不仅仅是一个inputstream和两个输出stream)。 Erlang包含强大的抽象,使得以类似于pipe道的方式表示并发进程变得容易。 Java提供了PipedInputStream和PipedOutputStream ,它们可以与线程一起使用,以更冗长的方式实现相同types的抽象。

你在看F#|>运算符吗? 我想你真的想要>>操作符。

我认为最根本的原因是因为C#和Java往往被用来构build更多的单片系统。 从文化angular度来说,即使想做pipe道式的东西也是不常见的 – 只要让应用程序实现必要的function即可。 build立多种简单工具然后以任意方式将它们粘合在一起的概念在这些情况下并不常见。

如果你看一些脚本语言,比如Python和Ruby,那么在这些脚本中做一些类似pipe道的东西,有一些非常好的工具。 例如,查看Pythonsubprocess模块,它允许您执行以下操作:

 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 

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

我在Python中构buildpipe道函数有很多乐趣。 我有一个我写的图书馆,我把这些内容和一个样本运行在这里 。 最适合我的是这个维基百科文章中描述的XML处理。

基于协程的stream式库在Haskell中已经存在了很长时间了。 两个stream行的例子是导pipe和pipe道 。

这两个图书馆都写得很好,文件齐全,相对成熟。 Yesodnetworking框架基于pipe道,而且速度非常快 。 Yesod在节目performance上与Node有竞争力,甚至在几个地方殴打它。

有趣的是,所有这些库默认都是单线程的。 这是因为stream水线的单一激励用例是I / O绑定的服务器。

您可以通过链接/过滤/转换迭代器来在Java中执行类似于pipe道的操作。 您可以使用Google的Guava迭代器 。

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

在斯卡拉它很容易使自己的pipe道运营商。

如果你仍然对答案感兴趣

你可以看看因素,或者更老的喜欢和连贯的范例。 在争论和出来的论点是隐含的,倾倒到一个堆栈。 那么下一个单词(函数)就会获取这个数据,并用它来做一些事情。

语法是后缀。

“123”打印

其中打印只需要一个参数,无论是在堆栈中。