используя Perl для создания простого калькулятора - PullRequest
0 голосов
/ 30 мая 2018

Мне нужно спроектировать простой калькулятор в Perl, который называется ex1.pl только с функциями +, -, * и /.Ниже приведено то, что у меня есть.

while (@ARGV>0) {
    if ($_=~m/(\d+)\s(.)\s(\d+)/) {
        if ($2 == "+") {
            print "$1 + $3\n";
        }   
        elsif ($2 == "-") {
            print "$1 - $3\n";
        }   
        elsif ($2 == "*") {
            print "$1 * $3\n";
        }   
        elsif ($2 == "/") {
            if ($3 == 0 ) { 
                print "$1 cannot be divided by 0\n";
            }   
            else {
                print "$1 / $3\n";
            }   
        }   
        else {
            print "operator not identified\n";
        }
    }   
    else {
        print "syntax error\n";
    }   
}

Например, если я наберу ./ex1.pl 5 + 2, он будет сообщать об ошибках, которые

Use of uninitialized value in pattern match (m//) at line 4

, пока я не нажму ctrl + c.Может кто-нибудь помочь мне определить, где я сделал не так?

1 Ответ

0 голосов
/ 30 мая 2018

Цикл 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), а затем обработать термин термином.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...