代码之家  ›  专栏  ›  技术社区  ›  cdiggins

为什么我看不到大多数高级语言中的管道操作符?

  •  41
  • cdiggins  · 技术社区  · 15 年前

    在Unix shell编程中, pipe operator 是一个非常强大的工具。通过一小部分核心实用程序、一种系统语言(如C)和一种脚本语言(如python),您可以构建非常紧凑和强大的shell脚本,这些脚本由操作系统自动并行。

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

    所以我的问题是,为什么我不看到类似于现代高级语言的UNIX管道,比如C语言、Java等等?是否有支持一流管道的语言(shell脚本除外)?这不是一个方便和安全的方式来表达并发算法吗?

    为了以防万一有人提起它,我看了一下f pipe forward操作符(forward pipe operator),它看起来更像一个函数应用程序操作符。据我所知,它将一个函数应用于数据,而不是将两个流连接在一起,但我愿意接受更正。

    后记 :在做一些关于实现协程的研究时,我意识到有一些相似之处。在一个 blog post 马丁·沃尔夫描述了一个与我相似的问题,但用的是协同作战而不是管道。

    13 回复  |  直到 9 年前
        1
  •  6
  •   Mark    15 年前

    在Erlang中可以很容易地执行管道类型并行。下面是2008年1月我的blogpost的无耻复制/粘贴。

    也, Glasgow Parallel Haskell 允许并行函数组合,相当于相同的东西,给你隐含的并行化。

    你已经在考虑 管道-“gzcat”怎么样 foo.tar.gz_tar xf-“?你可能不会 已经知道了,但外壳是 打开拉链 并行-stdin读取tar 阻止,直到数据由发送到stdout GZCAT。

    很多任务都可以表达出来 在管道方面,如果可以的话 这样做,然后得到某种程度的 与David平行化很简单 King's Helper代码(甚至整个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
  •  10
  •   Community Egal    7 年前

    哈哈!感谢我的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开头的值。

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

    它相当聪明,虽然这可能意味着它不是很蟒蛇,但它展示了你所追求的,以及一种很容易应用到其他语言的技巧。

        3
  •  5
  •   James Black    15 年前

    你可以在C语言和Java中找到一些类似管道的东西,例如,你在那里连接一个连接流并把它放在另一个连接流的构造函数中。

    你在Java中有:

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

    您可能需要查找链接输入流或输出流。

        4
  •  3
  •   gkcn    10 年前

    magrittr package 提供类似于r中f的pipe forward操作符的内容:

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

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

    iris %>%
      filter(Species == "virginica") %>%
      select(-Species) %>%
      colMeans
    
        5
  •  3
  •   ohaal soulmerge    9 年前

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

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

    • python管道: pypes
    • 莫扎特·奥兹可以用端口和螺纹制作管道。
        6
  •  2
  •   cdiggins    15 年前

    感谢所有伟大的回答和评论,以下是我学到的总结:

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

        7
  •  2
  •   Nathan Phillips    14 年前

    你在看f gt;接线员吗?我想你真的想要接线员。

        8
  •  1
  •   divegeek    15 年前

    我认为最根本的原因是因为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
    
        9
  •  1
  •   Dave DeLong    15 年前

    目标C具有 NSPipe 班级。我经常用它。

        10
  •  1
  •   Joel Bender    15 年前

    我在python中构建了很多有趣的管道函数。我有一个我写的图书馆,我放了内容和一个样本运行 here . 最适合我的是XML处理,如下面所述 Wikipedia article .

        11
  •  1
  •   Lambda Fairy    11 年前

    基于协程的流库已经在haskell中存在了一段时间。两个流行的例子是 conduit pipes .

    这两个图书馆都写得很好,有很好的文档记录,并且相对成熟。这个 Yesod Web框架是基于管道的, pretty damn fast .Yesod在性能上与node竞争,甚至在一些地方击败它。

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

        12
  •  0
  •   Adam Gent    13 年前

    通过链接/筛选/转换迭代器,可以在Java中执行类似管道的操作。 你可以使用谷歌的 Guava Iterators .

    我会说,即使是非常有用的番石榴库和静态导入,其结果仍然是大量的Java代码。

    在scala中,很容易创建自己的管道操作员。

        13
  •  0
  •   kobi7    10 年前

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

    您可以查看因子,或者查看旧的joy和forth以了解串联范式。 输入参数和输出参数是隐式的,转储到堆栈。然后下一个单词(函数)获取数据并对其进行处理。

    语法是后缀。

    “123”字

    其中print接受一个参数,堆栈中的任何参数。