Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Minimum command length matcher

by jcwren (Prior)
on Aug 29, 2000 at 18:44 UTC ( [id://30139]=sourcecode: print w/replies, xml ) Need Help??
Category: Miscellaneous
Author/Contact Info jcwren
jcwren@jcwren.com
Description: I write a fair number of programs that use interactive commands. I intensely dislike programs that require me to type more than the minimum number of characters to distinguish one command from another. I also dislike have to have special parameters that instruct the command interpeter what portion of the command is unique.

To that end, this snippet takes a hash of commands, keyed by the command name, and the user input, and finds the minimum match. Errors are no matches, or ambiguous matches, in which case an error message with the list of commands is returned. If no error, the full name of the hash is returned.
#!/usr/local/bin/perl -w

use strict;
use Carp;

my %Commands = ('who'      => \&do_who,
                'what'     => \&do_what,
                'why'      => \&do_why,
                'never'    => \&do_never,
                'again'    => \&do_again,
                'answer'   => \&do_answer);

{
   my $userinput = $ARGV[0];

   my ($errmsg, $command) = check_commands (\%Commands, $userinput);

   if ($errmsg)
   {
      print "$errmsg\n";
      exit;
   }

   &{$Commands {$command}};
}

sub check_commands
{
   @_ == 2 or croak "Incorrect number of parameters";
   
   my ($cmdhash, $command) = @_;
   
   my @matches = grep /^$command/i, sort keys %$cmdhash;
   
   if (scalar @matches == 0)
   {
      return ("I don't know what '$command' means", undef);
   }
   elsif (scalar @matches > 1)
   {
      my $lastcmd = pop @matches;

      return ("'$command' is ambigous.  It could mean " . join (', ', 
+@matches) . " or $lastcmd", undef);
   }
   
   return (undef, $matches [0]);
}

sub do_who     {print "Who\n"};
sub do_what    {print "What\n"};
sub do_why     {print "Why\n"};
sub do_never   {print "Never\n"};
sub do_again   {print "Again\n"};
sub do_answer  {print "Answer\n"};
Replies are listed 'Best First'.
RE: Minimum command length matcher
by KM (Priest) on Aug 29, 2000 at 18:53 UTC
    You could easily take this one step further and give it the ability to then accept which command the user meant (like some shells do). By changing the 'ambiguous match' (you have it 'ambigous', BTW) part to create a hash. Actually, if you make @matched into %matched with the structure of:

    %matches = (1 => 'match1', 2 => 'match2', etc... );

    You could then make the 'ambiguous match' section as which was meant.. ie..

    'monkey' is ambiguous. It could mean monkeylover [1] or monkeyman [2]:

    The user would then enter 1 or 2, you read it on STDIN, and run the correct sub. Just an idea :)

    Cheers,
    KM

(jcwren) RE: (2) Minimum command length matcher
by jcwren (Prior) on Aug 29, 2000 at 18:50 UTC
    Gee, Randal, I have no idea how it differs. Since I don't have time to know each and every module cataloged at CPAN and other places, sometimes I write my own stuff, even when it's only 10 lines.

    Update: And furthermore, Text::Abbrev doesn't do what I want. It gives me a hash of abbreviations, with the original strings as the values. Where exactly am I supposed to put the pointers to the functions to execute? No, that doesn't solve the problem at all. I suppose I could create the abbreviation hash from the command hash, but the sure seems like an extra step. And I don't get to use the nifty grep() function everyone carries on so much about. And even better, if the *user* should type more than the minimum number of characters required, it won't find the command. *BZZZZT* wrong answer!

    --Chris

    (And yes, I voted down your response for sarcasm)

    e-mail jcwren
RE (tilly) 2: Minimum command length matcher
by tilly (Archbishop) on Aug 29, 2000 at 19:49 UTC
    jcwren, what merlyn wanted was to have you build a hash lookup for the original command, and get the actual code-ref from a double lookup.

    Building an initial hash and working from that is clearly a nicer algorithm than grepping every time. However Text::Abbrev will not allow him to disambiguate the case where the match is ambiguous from simply not found. Now is that detail important? My take is that for anything meant for interactive use it is a critical distinction to make. It really matters to the UI.

    OTOH the algorithmic improvement is not dependent upon the library module. Untested:

    sub build_lookup { my %lookup; foreach my $str (@_) { foreach my $len (1..length($str)) { my $portion = substr($str, 0, $len); push @{$lookup{$portion}}, $str; } } return %lookup; }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://30139]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (5)
As of 2024-03-29 00:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found