Полезная информация

## 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();```