#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…
Надеюсь, это укажет вам правильное направление…