1
votes

Prendre strnig comme entrée pour le programme de calcul en Perl

Je suis nouveau sur Perl et j'essaie de créer un programme de calcul simple, mais les règles sont différentes des mathématiques normales. Toutes les opérations ont la même puissance et le problème mathématique doit être résolu de gauche à droite. Voici un exemple:

123 - 10 + 4 * 10 = ((123 - 10) + 4) * 10 = 1170

8 * 7/3 + 2 = ((8 * 7) / 3) + 2 = 20,666

Donc, dans le premier cas, l'utilisateur doit entrer une chaîne: 123 - 10 + 4 * 10. Comment aborder cette tâche? Je suis désolé si c'est trop une question générale, mais je ne sais même pas par où commencer. Ai-je besoin d'un compteur? Comme - chaque deuxième caractère de la chaîne est un opérateur, tandis que les deux sur les côtés sont des chiffres.


0 commentaires

3 Réponses :


1
votes

Pour être clair, vous ne recherchez pas une calculatrice ordinaire. Vous recherchez une calculatrice qui plie les règles des mathématiques.

Ce que vous voulez, c'est extraire les opérandes et les opérateurs, puis les traiter 3 à la fois, le premier étant la "somme" glissante, le second un opérateur et le troisième un opérande.

Un moyen simple de le gérer est d' eval simplement les chaînes. Mais étant donné que eval est une opération dangereuse, nous devons supprimer la souillure de l'entrée. Nous faisons cela avec une correspondance regex: /\d+|[+\-*\/]+/g . Cela correspond à 1 ou plusieurs + chiffres \d ou | , 1 ou plus + de +-*/ . Et nous faisons cette correspondance autant de fois que possible /g .

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

Vous voudrez peut-être ajouter une vérification d'entrée, compter les arguments et vous assurer qu'ils sont le nombre correct.

Une solution plus judicieuse consiste à utiliser une table d'appel, en utilisant l'opérateur comme clé d'un hachage de sous-marins conçu pour gérer chaque opération mathématique:

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")
}

Tant que l'argument du milieu est un opérateur, cela effectuera l'opération requise sans avoir besoin d' eval . Cela vous permettra également d'ajouter d'autres opérations mathématiques dont vous pourriez avoir besoin dans le futur.


3 commentaires

C'est génial, merci beaucoup pour votre aide. Je vais maintenant chercher un moyen de l'inverser - de le faire passer de droite à gauche, en tant que sous-tâche. Par exemple - 123 - 10 + 4 * 10 = 123 - (10 + (4 * 10)) = 73.


Vous êtes les bienvenus. Assurez-vous de cliquer sur le bouton Accepter si cela a résolu votre question.


L'inverse n'est pas réellement un défi, utilisez simplement la fonction reverse sur les arguments de la chaîne ... my ($main, @ops) = reverse / ... /g



-1
votes

Juste pour lire l'entrée brute de l'utilisateur, vous liriez simplement le descripteur de fichier STDIN.

@input = split /\s+/, $input;

Cela vous donnera une chaîne, dites "123 + 234 - 345" qui aura un marqueur de fin de ligne. Vous pouvez le supprimer en toute sécurité avec la commande chomp.

Après cela, vous voudrez analyser votre chaîne pour obtenir vos variables appropriées. Vous pouvez le forcer brutalement avec un analyseur de flux qui examine chaque caractère pendant que vous le lisez et le traite en conséquence. Par exemple:

@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
  }...
}

Une autre approche serait de le diviser en mots afin que vous puissiez simplement traiter des termes entiers.

$input = <STDIN>;

Au lieu d'un flux de caractères, lorsque vous traitez, les valeurs du tableau seront 123, +, 234, - et 345 ...

J'espère que ça vous indique la bonne direction...


0 commentaires

2
votes

J'ai peur d'être paresseux, donc je vais analyser avec une expression régulière et traiter pendant que j'analyse.

#!/usr/bin/env perl

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

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

my $result;
    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 $result;

Notez l'utilisation de (? {...}) 1


0 commentaires