Re: [SPOILER] Perl Quiz of the Week #24 (Turing Machine simulation)
Jon Ericson <[email protected]> Fri, 17 Sep 2004 12:27:50 -0700
| Newsgroups | gmane.comp.lang.perl.qotw.discuss |
|---|---|
| Organization | I speak for myself; not JPL, NASA nor the US Government |
| Message-ID | <[email protected]> |
Mark Jason Dominus <[email protected]> writes: >> if (/^(\w+)\s+(\w)\s+(\w+)\s+(\w)\s+([LR])\s*$/) > > 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. I thought about the regex solution, but I wanted better error messages. > Here's mine. I probably spent as much time fussing with the format of > the debugging output as I did with the rest of the program combined. > Not counting the debugging output section, the program is only 26 > lines long, and I don't think it is obfuscated. I wrote mine by writing test cases first -- stating with helloworld.tm. Getting the three examples working was straightforward. Coming up with all of the thing that can go wrong, took a bit more time. Thanks, Andrew Dalke for providing some really good tests. :-) CPAN has a Turing module (http://search.cpan.org/~grommel/Acme-Turing-0.01/), which I looked at after I had a solution. Unfortunately, it didn't yield many test cases. Here's a (significantly modified) example: start 0 maybe _ R start 1 erase 1 R maybe 1 erase 1 R maybe 0 maybe _ R maybe _ stop 0 R erase 1 erase _ R erase 0 erase _ R erase _ stop _ R I idly considered implementing a Turing Machine in Conway's Game of Life (see http://rendell.server.org.uk/gol/tm.htm), but that seemed like work. :-) I was wishing for a macro feature so that I could implement optional tracing, without hurting performance when tracing was turned off. I've been reading On Lisp (http://www.paulgraham.com/onlisp.html), and that seems like a pretty big advantage lisp has over perl at the moment. The other thing I miss is the binary // operator from <http://dev.perl.org/perl6/apocalypse/A03.html>, so I could say: $tape //= '_'; rather than: $tape = '_' unless defined $tape; I implemented the optional third argument specifying a tape position, but otherwise it's fairly similar to the other solutions that use an array for the tape. There's something very gratifying about a machine with only four parts: a tape (array), a state table (hash), a state register (scalar) and the tape head (scalar). #!/usr/bin/perl -w use strict; # Perl Quiz of the Week #24 (Turing Machine simulation) use Pod::Usage; pod2usage("$0: not enough arguments") unless @ARGV > 0; pod2usage("$0: too many arguments") if @ARGV > 3; # The third argument is the initial position of the tape head. my $head = pop if @ARGV == 3; $head = 0 unless defined $head; # The second argument is the initial state of the tape. my $tape = pop if @ARGV == 2; $tape = '_' unless defined $tape; die "tape may not contain '$1'" if $tape =~ /([^\w])/; my @tape = split(//, $tape); while ($head > $#tape) { push @tape, '_'; } while ($head < 0) { unshift @tape, '_'; $head++; } my %prog; my $state; my $file = pop; { no strict 'refs'; open($file, $file) or die "can't open $file: $!"; }; while (<$file>) { chomp; s/#.*//; my ($old, $read, $new, $write, $move) = split; next unless defined $old; # Most of these error checks aren't really needed. I included # them to comply with the spec. if ($old =~ /([^\w])/){ warn "instruction skipped since state may not contain '$1': '$_'"; next; }; die "state may not contain '$1'" if $new =~ /([^\w])/; die "this machine can only write one of A-z0-9_ to tape" unless $write =~ /^\w$/; unless ($read =~ /^\w$/){ warn "instruction will never be executed: '$_'"; next; }; # I'll forgive lowercase $move = uc($move); die "movement must be either 'L' or 'R'" unless ($move eq 'L' or $move eq 'R'); $state = $old unless defined $state; warn "new instruction '$_' replacing previous '$prog{$old}{$read}[3]'" if exists $prog{$old}{$read}; $prog{$old}{$read} = [$new, $write, $move, $_]; } close $file or warn "close of $file failed: $!"; die "no valid instructions found" unless defined $state; while (exists($prog{$state}{$tape[$head]})){ my @inst = @{$prog{$state}{$tape[$head]}}; $tape[$head] = $inst[1]; if ($inst[2] eq 'R') { $head++; $tape[$head] = '_' unless defined $tape[$head]; } else { $head--; if ($head < 0) { unshift @tape, '_'; $head = 0; } } $state = $inst[0]; } print format_tape(@tape), "\n"; exit (0); sub format_tape{ for (join('', @_)) { s/^_*//; s/_*$//; return $_; } }; __END__ =head1 NAME tm.pl - B<T>uring B<M>achine simulator =head1 SYNOPSIS B<tm.pl> I<program_file> [I<tape> [I<head_position>]] =head1 SEE ALSO I<http://article.gmane.org/gmane.comp.lang.perl.qotw.quiz-of-the-week/113> for a complete description. I<http://perl.plover.com/~alias/list.cgi?1:mss:2202:200409:pkjnoemnogpnbdkajdih> for details about the optional third argument. =head1 AUTHOR Jon Ericson I<[email protected]> =cut Thanks for a satisfying quiz, Jon