#!/usr/bin/perl -w # # lexmorph - change word1 to word2 # tchrist@perl.com # Mon Jul 27 09:24:41 MDT 1998 use strict; my $DEFWORDS = '/usr/dict/words'; $| = 1; my %Seen = (); my @Dict; my $Prune; my $Wordfile; use Getopt::Std; my %Opt; getopts('dnfimw:p:', \%Opt) or die <) { if (length() == $len + 1 && /^[a-z]+$iflag$/o) { chop; push @Dict, $iflag ? lc : $_; } } close DF; } sub change { my($src, $dst, $at, @path) = @_; my $found = 0; if ($src eq $dst) { print "FOUND: " if $Opt{'d'}; print "@path $src\n"; $Prune = @path unless $Opt{'n'}; # shorten tree depth delete $Seen{$src}; # functions need continue{}s return 1; } return 0 if @path > $Prune; # see whether the number of changes between src and dst are # farther away than our prune depth. $common is how many # characters are the same at corresponding possitions my $common = @{[ ($src ^ $dst) =~ /\0/g ] }; if (@path + length($dst) - $common > $Prune) { print "prune @path + $dst - $common > $Prune" if $Opt{'d'}; return 0; } print +(" " x @path) ."CHANGE " . "$src => $dst (@path)\n" if $Opt{'d'}; my (@good, @bad); # shuffle forward to make quick progress on "next" letter my @indices = (0 .. length($src) - 1); for (my $i = 0; $i <= $at; $i++) { push(@indices, shift @indices); } # find all matches at this level before descending. # that way we can separate the likely paths from the longer ones. for my $i (@indices) { my $pat = substr($src, 0, $i) . "[a-z]" . substr($src, 1 + $i); my @words = grep /^$pat$/, @Dict; my %done = ( $src => 1); for (@words) { next if $done{$_}++; # needed for dups in wordlist unless ($Seen{$_}) { # no loops back if (substr($_, $i, 1) eq substr($dst, $i, 1)) { push @{ $good[$i] }, $_; # closer to dest } else { push @{ $bad[$i] }, $_; # random change } } } } if ($Opt{'d'}) { print "$src: "; print join " ", map { @{ $good[$_] || []} } @indices; print " + "; print join " ", map { @{ $bad[$_] || []} } @indices; print "\n"; } # now recurse on the good list followed by the bad one. # the good list moves us immediately closer to the dest for my $ap ( \@good, \@bad ) { for my $i (@indices) { my @res = @{ $ap->[$i] || [] }; for my $word (@res) { $Seen{$src} = 1; $found += change($word, $dst, $i, @path, $src); $Seen{$src} = 0; } } } return $found; }