Schrift
Wiki:Tipp zum Debugging: use Data::Dumper; local $Data::Dumper::Useqq = 1; print Dumper \@var;
[thread]7035[/thread]

Prefix-Terme berechnen



<< |< 1 2 >| >> 20 Einträge, 2 Seiten
Ronnie
 2005-06-05 01:10
#55330 #55330
User since
2003-08-14
2022 Artikel
BenutzerIn
[default_avatar]
Ich überlege wie man mit Perl Terme im LISP-Stil lösen kann, wie z.B. (* 3 (+ 2 1) 4) was 36 ergeben würde. Grundlegend ist klar das es rekursiv gelöst werden könnte. Aber irgendwie weiß ich nicht wo ich anfangen soll. Wie handle ich z.B. am geschicktesten die geklammerten Ausdrücke? Regexp::Common::balanced ist wohl der falsche Ansatz.

Hier ein Ansatz der einen nicht geschachtelten Ausdruck berechnen kann, aber irgendwie fehlt mir der nächste Schritt?!
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/perl

use strict;
use warnings;

my $operators = {
'+' => sub { my $i=shift @_; $i += $_ for @_; return $i },
'-' => sub { my $i=shift @_; $i -= $_ for @_; return $i },
'*' => sub { my $i=shift @_; $i *= $_ for @_; return $i },
'/' => sub { my $i=shift @_; $i /= $_ for @_; return $i },
};

my $task = "/ 12 2 2";
print solve($task), "\n";
exit;

sub solve {
my $to_solve = shift @_ || die "solve(): no parameter\n";
my ($op, @vals) = split /\s+/, $to_solve;
die "solve(): wrong operator or to less values"
unless (exists $operators->{$op} && $#vals >= 1);
return $operators->{$op}(@vals);
}
pKai
 2005-06-05 02:00
#55331 #55331
User since
2005-02-18
357 Artikel
BenutzerIn
[default_avatar]
CPAN:Parse::RecDescent
CPAN:Parse::Yapp
I sense a soul in search of answers.
Ronnie
 2005-06-05 14:47
#55332 #55332
User since
2003-08-14
2022 Artikel
BenutzerIn
[default_avatar]
Ich habe einen Weg gefunden, der zwar nicht perfekt ist, aber meinen bescheidenen Bedürfnissen vorerst genügt.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/usr/bin/perl

use strict;
use warnings;

#use Data::Dumper;

my $operators = {
'+' => sub { my $i = shift @_; $i += $_ for @_; return $i },
'-' => sub { my $i = shift @_; $i -= $_ for @_; return $i },
'*' => sub { my $i = shift @_; $i *= $_ for @_; return $i },
'/' => sub { my $i = shift @_; $i /= $_ for @_; return $i },
};

my $task = "( + 3 4 ( * 2 7 ( + 1 1 ) ) ( / 6 2 ) )";
print calculate( split /\s+/, $task ), "\n";
exit;

sub solve {
my ( $op, @vals ) = @_;
die "solve(): wrong operator or to less values"
unless ( exists $operators->{$op} && $#vals >= 1 );
return $operators->{$op}(@vals);
}

sub calculate {
my @ops = @_;
@ops = @ops[ 1 .. $#ops - 1 ]
if $ops[0] eq '('
and $ops[-1] eq ')'; # remove leading and trailing brackets

my $marker = -1;
my $begin = undef;
my $end = undef;
my @marked = ();

for ( 0 .. $#ops ) {
$begin = $_ if $ops[$_] eq '(' && ++$marker == 0;
$end = $_ if $ops[$_] eq ')' && $marker-- == 0;
if ( defined $begin && defined $end ) {
push @marked, { begin => $begin,
end => $end }; # find balanced brackets on this level
$marker = -1;
$begin = undef;
$end = undef;
}
}

while (@marked) {
my $current = pop @marked;
splice @ops, $current->{begin}, # recursively solve inner brackets
$current->{end} - $current->{begin} + 1,
calculate( @ops[ $current->{begin} .. $current->{end} ] );
}

#print Dumper \@ops;
return solve(@ops);
}
\n\n

<!--EDIT|Ronnie|1117968563-->
sesth
 2005-06-05 17:58
#55333 #55333
User since
2005-02-01
181 Artikel
BenutzerIn
[default_avatar]
Ansonsten wird wohl Math-RPN das Gewünschte leisten.
Gruß
Thomas
pKai
 2005-06-05 18:27
#55334 #55334
User since
2005-02-18
357 Artikel
BenutzerIn
[default_avatar]
Die Notation die Ronnie auswerten will, ist aber keine RPN.
I sense a soul in search of answers.
kabel
 2005-06-06 01:04
#55335 #55335
User since
2003-08-04
704 Artikel
BenutzerIn
[default_avatar]
schade, wurde doch mehr code als ich dachte:
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/perl
use strict;
my $debug= 0;
my $lisp= "";
#
# S -> ( Op Opd* )
# Op -> + | * | - | /
# Opd -> S | Number
#
my @tokens;
my $pos= 0;

sub lisp {
        print "lisp_eval [$pos] token [$tokens[$pos]]\n"
               
 if $debug;
        # throw away (
        $pos ++;
        
        my $op= $tokens[$pos++];
        my @operands= ();
        while( $pos < scalar( @tokens ) ) {
               
 last if( $tokens[$pos] eq ")" );
               
 push @operands, operand();
        }

        return [$op, @operands];
}
sub operand {
        print "operand_eval [$pos] token [$tokens[$pos]]\n"
               
 if $debug;
        my $next= $tokens[$pos];
        if( $next =~ m/\d+/ ) {
               
 $pos ++;
               
 return $next;
        } else {
               
 return lisp();
        }
}

sub rewrite_perl {
        my @root= @{ shift() };
        my $op= $root[0];
        my @opds= ();
        foreach( 1 .. @root - 1 ) {
               
 if( ref $root[$_] eq "ARRAY" ) {
               
         push @opds, rewrite_perl( $root[$_] );
               
 } else {
               
         push @opds, $root[$_];
               
 }
        }
        my $code= $opds[0];
        foreach( 1 .. @opds - 1 ) {
               
 $code= "( $code $op $opds[$_] )";
        }
        return $code;
}
sub myread { $pos= 0; my $line= <STDIN>; @tokens= split /\s+/, $line; }

LOOP: print eval ( rewrite_perl lisp( myread ) ), $/; goto LOOP;
-- stefan
pKai
 2005-06-06 01:52
#55336 #55336
User since
2005-02-18
357 Artikel
BenutzerIn
[default_avatar]
Lösungsvorschlag mit Parse::RecDescent:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/user/bin/perl
use strict;
use warnings;
use Parse::RecDescent;

my $parser = new Parse::RecDescent (q(
{
my $operators = {
'+' => sub { my $i = shift @_; $i += $_ for @_; return $i },
'-' => sub { my $i = shift @_; $i -= $_ for @_; return $i },
'*' => sub { my $i = shift @_; $i *= $_ for @_; return $i },
'/' => sub { my $i = shift @_; $i /= $_ for @_; return $i },
};
}
line: paren /^\Z/
{ $item[1] }
paren: '(' op arg arg(s) ')'
{ $operators->{$item[2]}->($item[3], @{$item[4]}) }
op: '+' | '-' | '*' | '/'
arg: paren | /^[+-]?(?:\d+|\d*\.\d+)/
));

my $task = "(+3 4(*2 7(+1 -1))(/6 2))";
my $calc = $parser->line($task) || die "Syntax error in $task\n";
print $calc, $/;


Edit: Um sich den Restriktionen der oberen Lösungen (Leerzeichen) etwas anzunäheren, könnte in der Produktion für op erzwingen, dass nach dem Op keine Zahl folgen dar:
Code: (dl )
    op: /[-+*\/](?!\d)/
\n\n

<!--EDIT|pKai|1118056898-->
I sense a soul in search of answers.
kabel
 2005-06-06 22:26
#55337 #55337
User since
2003-08-04
704 Artikel
BenutzerIn
[default_avatar]
sehr schöner code :)
aber wie unterscheidet sich dein parser jetzt von meinem?!

;-)
-- stefan
Ronnie
 2005-06-07 01:23
#55338 #55338
User since
2003-08-14
2022 Artikel
BenutzerIn
[default_avatar]
@kabel: Kannst du ein paar erläuternde Worte zu deinem Code schreiben? pKai verwendet Parse::RecDescent womit ich mich leider auch noch nicht beschäftigt habe (habe kurz in die Doku gesehen und dann Kopfweh bekommen), aber deinen Ansatz verstehe ich leider auch nicht.
pKai
 2005-06-07 02:01
#55339 #55339
User since
2005-02-18
357 Artikel
BenutzerIn
[default_avatar]
Kabel macht quasi einen RecursiveDescent (rekursiven Abstieg) von Hand über die kontextfreie Sprache
Quote
# S -> ( Op Opd* )
# Op -> + | * | - | /
# Opd -> S | Number

, was das Modul aus der Angabe dieser Produktionen mit eingeflochtenen Berechnungsschritten "automatisch" macht.

Warum braucht man das? Es gibt in der Informatik eine Theorie der "formalen Sprachen", die zeigt, dass man solchen Ausdrücken mit beliebig tief verschachtelten Klammern nicht mehr mit regulären Ausdrücken beikommen kann.
Die zugehörige "Maschine" zu RegExen ist der "endliche Automat", der sich nichts nebenbei merken muss, während man bei diesen "kontextfreien Sprachen" (Klammersprachen) einen "Kellerautomat" (Stack) benötigt.

Die Lösungen von Kabel und mir sind Implementierungen dieses Prinzips.\n\n

<!--EDIT|pKai|1118095426-->
I sense a soul in search of answers.
<< |< 1 2 >| >> 20 Einträge, 2 Seiten



View all threads created 2005-06-05 01:10.