В первой части я представил методологию потокового программирования и ее основные принципы. Теперь пришло время рассмотреть немного подробнее.
Вот компонент или составной подпроцесс, о котором я говорил в предыдущей статье.
Обратите внимание, что подпроцесс 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))
Конец Применить
Мы могли бы также рассмотреть эквивалент замыканий: подпроцесс, в котором несколько входов были подключены заранее. Будут ли они нести оперативные данные или фиксированные данные, я не уверен. На данный момент это предмет дальнейших исследований.