#!/usr/bin/perl -w use strict; package Printer; use Carp; =head2 package Printer Printing machine. It takes its input and prints to STDOUT. =cut sub new { my ($class) = @_; bless {}, $class; } sub connect { my ($self, $self_socket, $peer, $peer_socket) = @_; croak "$self: already connected" if $self->{peer_socket}; croak "$self: no such socket '$self_socket'" if $self_socket ne 'input'; $self->{peer} = $peer; $self->{peer_socket} = $peer_socket; # ready to receive $peer->message($peer_socket); } sub message { my ($self, $self_socket, $object) = @_; croak "$self: no such socket '$self_socket'" if $self_socket ne 'input'; my ($peer, $peer_socket) = @$self{qw(peer peer_socket)}; print $object; # ready to receive $peer->message($peer_socket); } package RandGen; use Carp; =head2 package RandGen Random number generator machine. It generates random numbers and sends them to its output socket. =cut sub new { my ($class) = @_; bless {}, $class; } sub connect { my ($self, $self_socket, $peer, $peer_socket) = @_; croak "$self: already connected" if $self->{peer_socket}; croak "$self: no such socket '$self_socket'" if $self_socket ne 'output'; $self->{peer} = $peer; $self->{peer_socket} = $peer_socket; # wait for ready to receive } sub message { my ($self, $self_socket) = @_; croak "$self: no such socket '$self_socket'" if $self_socket ne 'output'; my ($peer, $peer_socket) = @$self{qw(peer peer_socket)}; $peer->message($peer_socket, rand); # wait for ready to receive } package Prompter; use Carp; =head2 package Prompter Prompting machine. It asks the user for lines of input. =cut sub new { my ($class, %args) = @_; bless { prompt => $args{prompt} }, $class; } sub connect { my ($self, $self_socket, $peer, $peer_socket) = @_; croak "$self: already connected" if $self->{peer_socket}; croak "$self: no such socket '$self_socket'" if $self_socket ne 'output'; $self->{peer} = $peer; $self->{peer_socket} = $peer_socket; # wait for ready to receive } sub message { my ($self, $self_socket) = @_; croak "$self: no such socket '$self_socket'" if $self_socket ne 'output'; my ($peer, $peer_socket) = @$self{qw(peer peer_socket)}; print $self->{prompt}; my $input = <>; $peer->message($peer_socket, $input); # wait for ready to receive } package Mapper; use Carp; =head2 package Mapper Mapping machine. It applies a perl expression or subroutine to an input and generates an output. =cut sub new { my ($class, %args) = @_; my ($exp, $sub) = @args{qw(exp sub)}; if ($exp) { $sub = eval qq{ sub { local \$_ = shift; return $exp; }; }; } bless { sub => $sub }, $class; } sub connect { my ($self, $self_socket, $peer, $peer_socket) = @_; if ($self_socket eq 'input') { croak "$self: input already connected" if $self->{input_socket}; $self->{input} = $peer; $self->{input_socket} = $peer_socket; } elsif ($self_socket eq 'output') { croak "$self: output already connected" if $self->{ouput_socket}; $self->{output} = $peer; $self->{output_socket} = $peer_socket; } else { croak "$self: no such socket '$self_socket'"; } # wait for ready to receive } sub message { my ($self, $self_socket, $object) = @_; if ($self_socket eq 'output') { my ($peer, $peer_socket, $sub) = @$self{qw(input input_socket)}; $peer->message($peer_socket); } elsif ($self_socket eq 'input') { my ($peer, $peer_socket, $sub) = @$self{qw(output output_socket sub)}; $object = &$sub($object); $peer->message($peer_socket, $object); } else { croak "$self: no such socket '$self_socket'"; } } package Adder; use Carp; =head2 package Adder Adding machine. It takes any number of inputs and adds them together. =cut sub new { my ($class) = @_; bless {}, $class; } sub connect { my ($self, $self_socket, $peer, $peer_socket) = @_; if ($self_socket eq 'input') { push @{$self->{inputs_and_sockets}}, $peer, $peer_socket; $self->{n_inputs} ++; } elsif ($self_socket eq 'output') { croak "$self: output already connected" if $self->{ouput_socket}; $self->{output} = $peer; $self->{output_socket} = $peer_socket; } else { croak "$self: no such socket '$self_socket'"; } # wait for ready to receive } sub message { my ($self, $self_socket, $object) = @_; if ($self_socket eq 'output') { $self->{total} = 0; $self->{count} = $self->{n_inputs}; my @inputs_and_sockets = @{$self->{inputs_and_sockets}}; while (my ($peer, $peer_socket) = splice @inputs_and_sockets, 0, 2) { $peer->message($peer_socket); } } elsif ($self_socket eq 'input') { $self->{total} += $object; if (--$self->{count} == 0) { my ($peer, $peer_socket) = @$self{qw(output output_socket)}; $peer->message($peer_socket, $self->{total}); } } else { croak "$self: no such socket '$self_socket'"; } } package main; my $printer = Printer->new; my $prompter = Prompter->new( prompt => 'Enter a number: ' ); my $randgen = RandGen->new; my $adder = Adder->new; my $mapper = Mapper->new( sub => sub { local $_ = shift; s/\./_/; "$_\n" } ); $prompter->connect('output', $adder, 'input'); $adder->connect('input', $prompter, 'output'); $adder->connect('output', $mapper, 'input'); $mapper->connect('input', $adder, 'output'); $randgen->connect('output', $adder, 'input'); $adder->connect('input', $randgen, 'output'); $mapper->connect('output', $printer, 'input'); $printer->connect('input', $adder, 'output');