Получение strnig в качестве входных данных для программы калькулятора на Perl

#string #perl #input #calculator

#строка #perl #ввод #калькулятор

Вопрос:

Я новичок в Perl и пытаюсь создать простую программу калькулятора, но правила отличаются от обычной математики. Все операции имеют одинаковую мощность, и математическая задача должна решаться слева направо. Вот пример:

123 — 10 4 * 10 = ((123 — 10) 4) * 10 = 1170

8 * 7 / 3 2 = ((8 * 7) / 3) 2 = 20.666

Итак, в первом случае пользователю необходимо ввести одну строку: 123 — 10 4 * 10 . Как мне подойти к этой задаче? Извините, если это слишком общий вопрос, но я не уверен, с чего даже начать. Нужен ли мне счетчик? Например, каждый второй символ строки является оператором, а два по бокам — цифрами.

Ответ №1:

Боюсь, я ленив, поэтому я буду анализировать с помощью регулярного выражения и обрабатывать по мере разбора.

 #!/usr/bin/env perl

#use Data::Dumper;
use Params::Validate (':all');
use 5.01800;
use warnings;

my $string=q{123 - 10   4 * 10};

my $resu<
    sub fee {
        my ($a)=validate_pos(@_,{ type=>SCALAR });
        #warn Data::Dumper->Dump([$a],[qw(*a)]),' ';
        $result=$a;
        };
    sub fi {
        my ($op,$b)=validate_pos(@_,{ type=>SCALAR},{ type=>SCALAR });
        #warn Data::Dumper->Dump([$op,$b],[qw(*op *b)]),' ';
        $result = $op eq ' ' ? $result $b :
                  $op eq '-' ? $result-$b :
                  $op eq '*' ? $result*$b :
                  $op eq '/' ? $result/$b :
                  undef;
        #warn Data::Dumper->Dump([$result],[qw(*result)]),' ';
        };

$string=~ m{^(d )(?{ fee($1) })(?:(?: *([- /*]) *)(d )(?{ fi($2,$3) }))*$};
say $resu<
  

Обратите внимание на использование (?{…}) 1

Ответ №2:

Чтобы было ясно, вы не ищете обычный калькулятор. Вы ищете калькулятор, который нарушает правила математики.

То, что вы хотите, это извлечь операнды и операторы, затем обработать их 3 одновременно, причем первый из них является скользящей «суммой», второй — оператором, а третий — операндом.

Простой способ справиться с этим — использовать только eval строки. Но поскольку eval это опасная операция, нам нужно определить входные данные. Мы делаем это с помощью совпадения регулярных выражений: /d |[ -*/] /g . Это соответствует либо 1 или более цифрам d | , либо 1 или более из -*/ них . И мы делаем это сопоставление столько раз, сколько можем /g .

 use strict;
use warnings;
use feature 'say';

while (<>) {                                     # while we get input
    my ($main, @ops) = /d |[ -*/] /g;         # extract the ops
    while (@ops) {                               # while the list is not empty
        $main = calc($main, splice @ops, 0, 2);  # take 2 items off the list and process
    }
    say $main;                                   # print result
}

sub calc {
    eval "@_";      # simply eval a string of 3 ops, e.g. eval("1   2")
}
  

Возможно, вы захотите добавить некоторую проверку ввода, чтобы подсчитать аргументы и убедиться, что они являются правильным числом.

Более разумным решением является использование вызывающей таблицы, использующей оператор в качестве ключа из хэша вложенных файлов, предназначенных для обработки каждой математической операции:

 sub calc {
    my %proc = (
        " " => sub { $_[0]   $_[1] },
        "-" => sub { $_[0] - $_[1] },
        "/" => sub { $_[0] / $_[1] },
        "*" => sub { $_[0] * $_[1] }
    );
    return $proc{$_[1]}($_[0], $_[2]);
}
  

Пока средний аргумент является оператором, это будет выполнять требуемую операцию без необходимости eval . Это также позволит вам добавлять другие математические операции, которые могут понадобиться в будущем.

Комментарии:

1. Это здорово, большое спасибо за помощь. Теперь я буду искать способ изменить это — заставить его двигаться справа налево, как подзадача. Например — 123 — 10 4 * 10 = 123 — ( 10 ( 4 * 10 ) ) = 73.

2. Пожалуйста. Обязательно нажмите кнопку «Принять», если это решило ваш вопрос.

3. Обратное на самом деле не является проблемой, просто используйте reverse функцию для аргументов из строки… my ($main, @ops) = reverse / ... /g

Ответ №3:

Просто чтобы прочитать необработанный ввод от пользователя, вам нужно просто прочитать дескриптор файла STDIN.

 $input = <STDIN>;
  

Это даст вам строку, скажем «123 234 — 345 » у которого будет маркер конца строки. Вы можете безопасно удалить это с помощью команды chomp.

После этого вы захотите проанализировать свою строку, чтобы получить соответствующие переменные. Вы можете выполнить это методом перебора с помощью stream scanner, который просматривает каждый символ по мере его чтения и обрабатывает его соответствующим образом. Например:

 @input = split //, $input;
for $ch (@input) {
  if ($ch > 0 and $ch <= 9) {
    $tVal = ($tVal * 10)   $ch;
  } elsif ($ch eq " ") {
    $newVal = $oldVal
  } elsif ($ch eq " ") {
    # Do addition stuff
  }...
}
  

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

 @input = split /s /, $input;
  

Вместо потока символов при обработке значения массива будут 123, , 234, — и 345…

Надеюсь, это укажет вам правильное направление…