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

cdi*_*ins 42 language-agnostic shell concurrency programming-languages pipe

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

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

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

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

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

Chr*_*utz 10

哈哈!感谢我的Google-fu,我找到了一个你可能感兴趣的答案.基本上,答案是反对"不要重载运算符,除非你真的必须"通过重载按位运算符来提供类似shell的管道的参数,导致Python代码如下:

for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7):
    print i
Run Code Online (Sandbox Code Playgroud)

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

for i in xrange(2,100) | strify() | startswith(5):
    print i
Run Code Online (Sandbox Code Playgroud)

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

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

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


Mar*_*ick 6

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

此外,格拉斯哥Parallel Haskell允许并行功能组合,这相当于一件事,为您提供隐式并行化.

你已经考虑过管道 - "gzcat foo.tar.gz | tar xf - "怎么样?您可能不知道它,但shell正在并行运行解压缩和解压缩 - 在tar中读取的stdin只是阻塞,直到数据被gzcat发送到stdout.

很多任务都可以用管道来表达,如果你能做到这一点,那么使用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]).
Run Code Online (Sandbox Code Playgroud)

因此,基本上他在这里所做的就是制作一个步骤列表 - 每个步骤都在一个乐趣中实现,无论前一步输出如何都可以接受输入(当然,乐趣甚至可以内联定义).请查看David的博客文章,了解代码和更详细的说明.


gkc*_*kcn 6

magrittr 提供了类似于 R 中 F# 的管道转发运算符的内容:

rnorm(100) %>% abs %>% mean
Run Code Online (Sandbox Code Playgroud)

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

iris %>%
  filter(Species == "virginica") %>%
  select(-Species) %>%
  colMeans
Run Code Online (Sandbox Code Playgroud)


Jam*_*ack 5

例如,您可以在C#和Java中找到管道之类的东西,在这里您可以获取连接流并将其放在另一个连接流的构造函数中.

所以,你有Java:

new BufferedReader(new InputStreamReader(System.in));
Run Code Online (Sandbox Code Playgroud)

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

  • 然后您只需使用PipedInput(输出)流:http://java.sun.com/javase/6/docs/api/java/io/PipedInputStream.html,但这不是并行的,但您可以将数据转换为你经过溪流. (2认同)

cdi*_*ins 5

感谢所有精彩的答案和评论,以下是我所学到的总结:

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