Recipe 6.20. Matching Abbreviations
Author: Tom Christiansen & Nathan Torkington
Date: 1999-07-02T01:35:06Z

#

| Chapter 6 Pattern Matching |

## 6.20. Matching Abbreviations

### Problem

Suppose you had a list of commands, such as `"send"`, `"abort"`, `"list"`, and `"edit"`. The user types one in, but you don't want to make them type out the whole thing.

### Solution

You can use the following technique if the strings all start with different characters or if you want to arrange the matches so one takes precedence over another, as `"SEND"` has precedence over `"STOP"` here:

    chomp($answer = <>);
    if    ("SEND"  =~ /^\Q$answer/i) { print "Action is send\n"  }
    elsif ("STOP"  =~ /^\Q$answer/i) { print "Action is stop\n"  }
    elsif ("ABORT" =~ /^\Q$answer/i) { print "Action is abort\n" }
    elsif ("LIST"  =~ /^\Q$answer/i) { print "Action is list\n"  }
    elsif ("EDIT"  =~ /^\Q$answer/i) { print "Action is edit\n"  }

Or you can use the Text::Abbrev module:

    use Text::Abbrev;
    $href = abbrev qw(send abort list edit);
    for (print "Action: "; <>; print "Action: ") {
        chomp;
        my $action = $href->{ lc($_) };
        print "Action is $action\n";
    }

### Discussion

The first technique switches the typical order of a match. Normally you have a variable on the left side of the match and a known pattern on the right side. We might try to decide which action the user wanted us to take by saying `$answer` `=~` `/^ABORT/i`, which is true if `$answer` begins with the string `"ABORT"`. It matches whether `$answer` has anything after `"ABORT"`, so `"ABORT` `LATER"` would still match. Handling abbreviations generally requires quite a bit of ugliness: `$answer` `=~` `/^A(B(O(R(T)?)?)?)?$/i`.

Compare the classic `"variable` `=~` `pattern"` with `"ABORT"` `=~` `/^\Q$answer/i`. The `\Q` escapes any characters that would otherwise be treated as regular expression, so your program won't blow up if the user enters an invalid pattern. When the user enters something like `"ab"`, the expanded match becomes `"ABORT"` `=~` `/^ab/i` after variable substitution and metaquoting. This matches.

The standard Text::Abbrev module takes a different approach. You give it a list of words, and it returns a reference to a hash whose keys are all unambiguous abbreviations and whose values are the fully expanded strings. So if `$href` were created as in the Solution example, `$href->{$var}` would return the string `"abort"`.

This technique is commonly used to call a function based on the name of the string the user types in. Do this by using a symbolic reference, like:

    $name = 'send';
    &$name();

But that's scary, because it allows the user to run any function in our program, assuming they know its name. It also runs afoul of that pesky `use` `strict` `'refs'` pragma.

Here's a partial program that creates a hash in which the key is the command name and the value is a reference to the function to call for that command:

    # assumes that &invoke_editor, &deliver_message,
    # $file and $PAGER are defined somewhere else.
    use Text::Abbrev;
    my($href, %actions, $errors);
    %actions = (
        "edit"  => \&invoke_editor,
        "send"  => \&deliver_message,
        "list"  => sub { system($PAGER, $file) },
        "abort" => sub {
                        print "See ya!\n";
                        exit;
                   },
        ""      => sub {
                        print "Unknown command: $cmd\n";
                        $errors++;
                   },
    );

    $href = abbrev(keys %actions);

    local $_;
    for (print "Action: "; <>; print "Action: ") {
        s/^\s+//;       # trim leading  white space
        s/\s+$//;       # trim trailing white space
        next unless $_;
        $actions->{ $href->{ lc($_) } }->();
    }

The last statement could have been written like this if you're not into tight expressions or need practice typing:

    $abbreviation = lc($_);
    $expansion    = $href->{$abbreviation};
    $coderef      = $actions->{$expansion};
    &$coderef();

### See Also

The documentation for the standard Text::Abbrev module (also in Chapter 7 of Programming Perl ); interpolation is explained in the "Scalar Value Constructors" section of perldata (1), and in the "String literals" section of Chapter 2 of Programming Perl

6.19. Matching a Valid Mail Address |  | 6.21. Program: urlify