[SPOILER] turning machine solution
Colin Rafferty <colin.rafferty-/PgpppG8B+R7qynMiXIxWgC/[email protected]> Fri, 17 Sep 2004 10:43:45 -0400
| Newsgroups | gmane.comp.lang.perl.qotw.discuss |
|---|---|
| Message-ID | <[email protected]> |
I thought that the most interesting part of the design was the tape.
So I made it a class. Of course, for an infinite tape, the most
obvious solution was a hash from index to character.
I thought about having the tape never hold blanks, but in the end,
found it easier to allow that, and get rid of them before I output the
tape.
The transitions are of course a hash from [begin state, input] to
[output, end state, direction]. I check to make sure that there are
no duplicate transitions. Maybe I should have made this an NFSM?
That would be ugly, since I would need to have not just a set of
current states (including tape position), but a set of current tapes.
Fortunately, a non-deterministic turing machine is turing complete, so
I can leave it as an exercise for the student.
Of course, my program has an optional third parameter specifying the
initial position.
use strict;
sub Tape::new
{
my ($class, $init, $init_loc) = @_;
my %cells;
my $curr = 0;
$cells{$curr++} = $_ for split '', $init;
my $self = { cells => \%cells,
curr => $init_loc };
bless($self, $class);
return $self;
}
sub Tape::read
{
my ($self) = @_;
return '_' unless exists $self->{cells}->{$self->{curr}};
return $self->{cells}->{$self->{curr}};
}
sub Tape::write
{
my ($self, $char) = @_;
$self->{cells}->{$self->{curr}} = $char;
}
sub Tape::move
{
my ($self, $dir) = @_;
$self->{curr} += (($dir eq 'L') ? -1 : 1);
}
sub Tape::output
{
my ($self) = @_;
$self->remove_blanks();
my @keys = keys %{$self->{cells}};
return "" unless scalar(@keys);
my ($start, $end) = (sort { $a <=> $b } @keys)[0, -1];
return join '', map {
exists($self->{cells}->{$_}) ? $self->{cells}->{$_} : '_'
} ($start .. $end);
}
sub Tape::remove_blanks
{
my ($self) = @_;
$self->{cells}->{$_} eq '_' and delete $self->{cells}->{$_}
for keys %{$self->{cells}};
}
# sub main
{
my ($file, $init, $init_loc) = @ARGV;
################################################################
#
# Handle Inputs
#
################################################################
die "Usage: turing <file> [<initstring> [<initposition>]]\n"
unless defined $file;
$init = "" unless defined $init;
$init_loc = 0 unless defined $init_loc;
################################################################
#
# Initialize States.
#
################################################################
my %states;
my $curr_state;
open FH, "< $file" or die "cannot open $file for reading: $!\n";
while (<FH>)
{
chomp;
s/\s*(#.*)?$//; # remove trailing spaces and comments
next if /^$/; # skip blanks
my ( $start, $input, $end, $output, $dir) =
/^\s*(\w+)\s+(\w)\s+ (\w+)\s+(\w)\s+ ([LR])$/x
or die "invalid input line $.\n";
die "duplicate input [$start, $input] on line $.\n"
if exists $states{$start}{$input};
$states{$start}{$input} = { end => $end,
output => $output,
dir => $dir };
$curr_state = $start unless defined $curr_state;
}
################################################################
#
# Initialize Tape.
#
################################################################
my $tape = new Tape($init, $init_loc);
################################################################
#
# Run machine.
#
################################################################
while (defined (my $transition = $states{$curr_state}{$tape->read()}))
{
$tape->write($transition->{output});
$tape->move($transition->{dir});
$curr_state = $transition->{end};
}
################################################################
#
# Print Tape.
#
################################################################
print $tape->output(), "\n";
}
# end file turing