В первой части я представил методологию потокового программирования и ее основные принципы. Теперь пришло время рассмотреть немного подробнее.

Вот компонент или составной подпроцесс, о котором я говорил в предыдущей статье.

Обратите внимание, что подпроцесс Append используется дважды. Как Append узнает, какие два входа нужно объединить, а какой выход использовать? Дублируется ли подпроцесс для одновременного запуска в двух конфигурациях?

Короткий ответ: да".

Нет ограничений на то, сколько раз тот или иной подпроцесс появляется в сети процесса, и каждое появление может иметь разные связи.

Другой вопрос: «Может ли быть больше одного выхода?»

Дж. Пол Моррисон определенно так думает. На самом деле, одним из первых компонентов, которые он представляет в своей книге, является Selector, для которого он делает два вывода.

Однако у нас есть проблема. В текстовой нотации, которую я ввел ранее, обычный оператор «Return» в традиционном программировании был заменен вызовом «Push», но результат был тот же: он допускает только один выходной канал.

Ограничивает ли это использование моего варианта потокового программирования?

Я верю, что да. Желательно разрешить несколько каналов вывода, чтобы раскрыть реальную мощь потокового программирования.

Было бы достаточно просто добавить в Push параметр, указывающий, в какой выходной канал данные должны быть отправлены, но как соединить эти каналы? Знакомый синтаксис, который выглядит как вызов функции, больше не будет работать:

Добавить(Добавить(Левая часть, Приклеить), Правая часть)

Одним из решений было бы передать дополнительные выходные каналы в качестве входных данных для компонента. Что-то вроде этого:

Определить селектор как подпроцесс
- x как число = ?
- Отклонить как вывод = ?
- Если x ≥ 0
- - Push(x)
- Else< br /> - - Push(Reject, x)
- End if
End Selector

И для компонента, использующего Selector:

Ошибка как новый вывод
Push(SquareRoot(Selector(value, Error)))
Report("Квадратный корень требует положительного числа:", Error.number)

Если значение положительное, селектор отправляет его в SquareRoot, в противном случае он отправляет его в Report (используя конвейер ошибок). Обратите внимание, что в коде нет If-Else, программист только устанавливает соединения, но не может устанавливать порядок. В потоковом программировании именно данные определяют, если и когда выполняется подпроцесс.

На рисунке выше показаны соединения. Я думаю, это тот случай, когда рисунок передает вещи лучше, чем текст.

Мы только что видели пример отправки выходного канала в подпроцесс, который на самом деле не показан на рисунке — это скорее техническое решение наличия нескольких выходных каналов, чем реальность проекта. Что еще можно отправить в подпроцесс? Можно ли отправить подпроцесс через канал ввода?

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

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

Шутки в сторону, довольно просто добавить входные данные подпроцесса и использовать их в компоненте:

Определить Применить как Подпроцесс
- Преобразовать как Подпроцесс = ?
- n как Число = ?
- Нажать(Преобразовать(n))
Конец Применить

Мы могли бы также рассмотреть эквивалент замыканий: подпроцесс, в котором несколько входов были подключены заранее. Будут ли они нести оперативные данные или фиксированные данные, я не уверен. На данный момент это предмет дальнейших исследований.