Цикл while(@ARGV>0)
не присваивает $_
, в котором выполняется сопоставление с регулярным выражением, поэтому ничего не сопоставляется, и код переходит к else
.Кроме того, @ARGV
никогда не очищается, поэтому код находится в бесконечном цикле.
Остальная часть кода синтаксически правильна, но должна быть переписана, так как она перебирает слова во входных данных, в то время как регулярное выражение записывается вобработать все это.Либо обработка должна принимать каждый аргумент по очереди, либо командная строка должна быть собрана в строку для этого регулярного выражения.
Дополнительные комментарии по коду в вопросе опубликованы ниже.
Позвольте мне предложить другой подход
use warnings;
use strict;
use feature 'say';
use Scalar::Util qw(looks_like_number);
my ($n1, $op, $n2) = @ARGV;
my $re_oper = qr{^(?:\+|-|\*|/)$}; #/
usage() if @ARGV != 3
or not looks_like_number($n1) or not looks_like_number($n2)
or $op !~ $re_oper;
my %calculate = (
'+' => sub { return $_[0] + $_[1] },
'-' => sub { return $_[0] - $_[1] },
'*' => sub { return $_[0] * $_[1] },
'/' => sub {
die "Can't divide by zero" if $_[1] == 0;
return $_[0] / $_[1]
},
);
say $calculate{$op}->($n1, $n2);
sub usage {
say STDERR
"Usage: $0 number operator number\n",
"The \"operator\" is one of +,-,\\*,/\n",
"Note that multiplication (*) must be escaped at command line";
exit;
}
Аргументы копируются и проверяются на наличие ошибок, используя looks_like_number
из Scalar :: Util и регулярное выражение.Я подготавливаю шаблон регулярных выражений, используя оператор qr
, чтобы он был указан отдельно, что облегчает обслуживание и расширение.
Затем мы определяем хеш с анонимными подпрограммами (ссылки на код см.item 4. in Создание ссылок в perlref ) в виде значений, часто называемых таблицей отправки .Таким образом, нет необходимости в каскадной серии if-elsif
: для данного ключа (оператора) соответствующее значение разыменовывается, и эта подпрограмма выполняется.
Аргументы проверяются в одном выражении для краткости, но это лучше делать по одному, чтобы иметь возможность сообщать пользователю о точной совершенной ошибке (и приводить кавычки, вызвавшие ее).
Еще несколько подробностей о размещенном коде
Захваченный шаблон для оператора необходимо проверить с использованием string сравнение, что делается с eq
оператор, не с ==
.Смотрите эти в perlop .Итак, if ($2 eq '+')
и т. Д.
По умолчанию выполняется сопоставление регулярному выражению с переменной $_
, которая по умолчанию используется во многих Perl.Поэтому нет необходимости писать if ($_ =~ /.../)
, а просто if (/.../)
.Гораздо понятнее прочитать, что оправдывает (неявное) использование $_
Шаблон регулярного выражения \d
захватывает все виды цифр, включая Юникод.Лучше использовать [0-9]
Шаблон \s
допускает один символ «пробел».Если вы объедините аргументы командной строки в строку с помощью $input = join ' ', @ARGV;
(чтобы использовать для этого регулярное выражение), то это нормально.Но все же безопаснее предусмотреть больше пробелов с \s+
.
* В регулярном выражении .
соответствует любому одному символу.Это ограничивает возможное расширение сценария в будущем, например, для повышения до **
(и т. Д.).Подумайте о том, чтобы разрешить любые символы между числами, что возможно, поскольку они разделены в командной строке пробелом.
Шаблон, используемый для чисел, соответствует только натуральным числам (при случайном разрешении первогобыть отрицательным, если ему предшествует минус).Приведенный выше код заботится об этом, используя looks_like_number
, который использует все, что может сделать Perl для распознавания числа.
Оригинальная версия вопроса имел while (<>)
для цикла, и ответ начинался с этого :
Оператор "diamond" (<>
) читает из файлов, представленных в командестрока
Ввод из <>
поступает либо из стандартного ввода, либо из каждого файла, указанного в командной строке.
, поэтому ваш ввод 5 + 2
принимается заимена файлов 5
и +
и 2
, и такие файлы не существуют.
Этот оператор более сложен, о чем можно прочитать в документации.
Доступ к аргументам командной строки можно получить в @ ARGV , поэтому самое простое решение - заменить while (<>)
на foreach (@ARGV)
, а затем обработать термин термином.