Подтвердить что ты не робот

Почему я не вижу операторов труб на большинстве языков высокого уровня?

В программном обеспечении Unix оболочки оператор является чрезвычайно мощным инструментом. С небольшим набором основных утилит, системным языком (например, C) и языком сценариев (например, Python) вы можете создавать чрезвычайно компактные и мощные сценарии оболочки, которые автоматически распараллеливаются операционной системой.

Очевидно, что это очень мощная парадигма программирования, но я не видел трубы как абстракции первого класса на любом языке, кроме оболочки script. Код, необходимый для репликации функциональности скриптов с использованием труб, кажется, всегда будет довольно сложным.

Итак, мой вопрос: почему я не вижу что-то похожее на трубы Unix на современных языках высокого уровня, таких как С#, Java и т.д.? Существуют ли языки (кроме сценариев оболочки), которые поддерживают протоколы первого класса? Разве это не удобный и безопасный способ выражения параллельных алгоритмов?

На всякий случай кто-то его подведет, я посмотрел на оператора прямой линии F # (оператор прямой трубы) и больше похож на оператор приложения функции. Насколько я могу судить, он применяет функцию к данным, а не соединяет два потока вместе, но я открыт для исправлений.

Postscript. Проводя некоторые исследования по внедрению сопрограмм, я понимаю, что есть определенные параллели. В блоге Мартин Вольф описывает аналогичную проблему для моего, но с точки зрения сопрограмм вместо труб.

4b9b3361

Ответ 1

В Erlang вы можете легко создать тип конвейеризации parallelism. Ниже приведена бесстыдная копия/вставка из моего блога в январе 2008 года.

Кроме того, Glasgow Parallel Haskell позволяет создавать параллельную функцию, которая составляет одно и то же, что дает вам неявное распараллеливание.

Вы уже думаете в терминах трубопроводы - как насчет "gzcat foo.tar.gz | tar xf -"? Вы не можете знали это, но оболочка запуск распаковки и распаковки в параллельно - stdin читается в tar просто блоков, пока данные не будут отправлены в стандартный вывод gzcat.

Ну, много задач может быть выражено в плане трубопроводов, и если вы можете сделайте это, затем получите некоторый уровень параллелизация проста с Дэвидом Код поддержки короля (даже через 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]).

Так что в основном то, что он здесь делает составление списка шагов - каждый шаг воплощение в принимает в качестве входных данных любые предыдущие ступенчатые выходы (удовольствие может даже быть определенная строка, конечно). Go check Давид запись в блоге для код и более подробное объяснение.

Ответ 2

Ха-ха! Благодаря моему Google-fu я нашел ответ SO, который может вас заинтересовать. В основном, ответ идет против аргумента "не перегружайте операторы, если вам действительно нужно", перегружая оператор поразрядного ИЛИ, чтобы обеспечить оболочечный конвейер, в результате чего код 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() использует метод map для преобразования всего в строку, а sieve() использует метод filter для отсеивания вещей, которые не являются кратными числу.

Это довольно умно, хотя, возможно, это означает, что он не очень Pythonic, но он демонстрирует, что вам нужно, и метод его получения, который, вероятно, может быть легко применен к другим языкам.

Ответ 3

Вы можете найти что-то вроде труб в С# и Java, например, где вы берете поток соединения и помещаете его в конструктор другого потока соединения.

Итак, у вас есть Java:

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

Возможно, вам захочется найти цепочку входных потоков или выходных потоков.

Ответ 5

Благодаря всем замечательным ответам и комментариям, вот краткое изложение того, что я узнал:

Оказывается, существует целая парадигма, связанная с тем, что меня интересует: Flow-based programming. Хорошим примером языка, разработанного специально для потокового программирования, является конвейеры Hartmann. Конвейеры Hartamnn обобщают идею потоков и труб, используемых в Unix и других ОС, для обеспечения множества входных и выходных потоков (а не только одного входного потока и двух выходных потоков). Erlang содержит мощные абстракции, которые позволяют легко выражать параллельные процессы таким образом, который напоминает трубы. Java предоставляет PipedInputStream и PipedOutputStream, который может использоваться с потоками для более точного описания абстракций.

Ответ 6

Вы смотрите на оператор F # | > ? Я думаю, что вам действительно нужен оператор → .

Ответ 7

Я думаю, что самая фундаментальная причина заключается в том, что С# и 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

Objective-C имеет класс NSPipe. Я использую его довольно часто.

Ответ 9

Обычно вам это просто не нужно, и программы работают быстрее без него.

В основном трубопровод представляет собой образец потребителя/производителя. И не так сложно писать этих потребителей и производителей, потому что они не имеют большого количества данных.

  • Piping for Python: pypes
  • Mozart-OZ может выполнять соединения с использованием портов и потоков.

Ответ 10

В Python мне было очень весело работать с конвейерными функциями. У меня есть библиотека, которую я написал, я разместил содержимое и образец здесь. Лучше всего для меня была обработка XML, описанная в этой статье Википедии.

Ответ 11

В Haskell уже довольно давно существуют потоковые библиотеки на основе сопрограмм. Два популярных примера: conduit и pipes.

Обе библиотеки хорошо написаны и хорошо документированы и относительно зрелы. Yesod веб-каркас основан на кабелепроводе, а довольно чертовски быстро. Йесод конкурирует с Node от производительности, даже избивая его в нескольких местах.

Интересно, что все эти библиотеки по умолчанию имеют однопоточность. Это связано с тем, что единственным мотивирующим вариантом использования для трубопроводов являются серверы, которые связаны с I/O.

Ответ 12

Вы можете выполнять операции с подобными трубами в Java путем цепочки/фильтрации/преобразования итераторов. Вы можете использовать Google Итераторы Guava.

Я скажу, что даже с очень полезной библиотекой guava и статическими импортами она по-прежнему заканчивается большим количеством Java-кода.

В Scala его довольно легко сделать собственный оператор трубы.

Ответ 13

если вы все еще интересуетесь ответом...

вы можете посмотреть на фактор, или на старшую радость и вперед для конкатенирующей парадигмы. в аргументах и ​​выводах аргументы неявны, сбрасываются в стек. то следующее слово (функция) принимает эти данные и что-то делает с ним.

синтаксис является постфиксным.

"123" print

где print принимает один аргумент, независимо от того, что находится в стеке.

Ответ 14

Вы можете использовать мою библиотеку в Python: github.com/sspipe/sspipe