[SPOILER] Perl Quiz of the Week #24 (Turing Machine simulation)
Tom Varga <[email protected]> Fri, 17 Sep 2004 10:37:02 -0400 (EDT)
| Newsgroups | gmane.comp.lang.perl.qotw.discuss |
|---|---|
| Message-ID | <[email protected]> |
Interesting.
> I'm surprised that all four of the sample solutions posted so far use
> a complicated-looking regex like this instead of going for 'split',
> seemed to me to be the obvious and straightforward technique.
Well, I interpreted the following :
> And let's also say that we can give symbolic names of the form /\w+/
> to the values that can be stored in the state register.
to mean that a state name that looks like !@$#* wouldn't be legal! :) I also
wanted to make sure that the program uses legal direction names too.
Anyway, here is my solution. I too decided on the hash as the easiest
solution. On the whole, surprisingly easy. :)
-Tom
#!/usr/local/bin/perl -w
use strict ;
my (%state, %tape, $current_state, $the_line) ;
my ($tape_pointer, $pos, $program_file, $tape) = (0, 0, @ARGV, '') ;
$tape=~ s/^_+// ; ## Remove leading _ from tape
map($tape{$pos++}=$_ , split(//, $tape)) ; ## Convert to hash for easy negative position handling
open(PROGRAM, $program_file) || die "ERROR : Unable to open $program_file : $!\n" ;
while (defined($the_line = <PROGRAM>)) { ## Store states
$the_line =~ s/\#.*$// ; ## Remove comments
$the_line =~ s/^\s+// ; ## Remove leading spaces
next if $the_line =~ /^\s*$/ ; ## Skip empty lines
if ($the_line =~ /(\w+)\s+(\w)\s+(\w+)\s+(\w)\s+([RL])/i) {
my ($this_state, $this_value, $next_state, $next_value, $dir) = ($1, $2, $3, $4, $5) ;
$current_state = $this_state unless $current_state ;
$state{$this_state}{$this_value}{next_state} = $next_state ;
$state{$this_state}{$this_value}{next_value} = $next_value ;
$state{$this_state}{$this_value}{dir} = ($dir =~ /R/i) ? 1 : -1 ;
} else {
die "ERROR : Do not understand the following line : $the_line\n" ;
}
}
while (defined($state{$current_state})) { ## Run the program
$tape{$tape_pointer} = '_' unless defined($tape{$tape_pointer}) ;
my $current_value = $tape{$tape_pointer} ;
my $next_value = $tape{$tape_pointer} = $state{$current_state}{$current_value}{next_value} ;
$tape_pointer += $state{$current_state}{$current_value}{dir} ;
$current_state = $state{$current_state}{$current_value}{next_state} ;
}
## Print the result ##
undef $tape ;
foreach my $i (sort {$a <=> $b} (keys(%tape))) {
$tape .= $tape{$i} ;
}
$tape =~ s/_+$// ; ## Clean up leading and trailing _
$tape =~ s/^_+// ;
print "$tape\n" ;