Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Solving lisp-style terms

by neniro (Priest)
on Jun 05, 2005 at 10:54 UTC ( [id://463700]=CUFP: print w/replies, xml ) Need Help??

I was searching for a way to solve simple lisp-style terms in prefix-notation. My solution isn't very robust, but it shows a simple way to solve this kind of problem.
#!/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 lev +el $marker = -1; $begin = undef; $end = undef; } } while (@marked) { my $current = pop @marked; splice @ops, $current->{begin}, # recursively solve inner b +rackets $current->{end} - $current->{begin} + 1, calculate( @ops[ $current->{begin} .. $current->{end} ] ); } #print Dumper \@ops; return solve(@ops); }

Replies are listed 'Best First'.
Re: Solving lisp-style terms
by Ovid (Cardinal) on Jun 05, 2005 at 14:49 UTC

    Nice job! I admit, though, that it seems a bit complicated to me. Here's a rough cut of a simpler version.

    #!/usr/bin/perl use strict; use warnings; my %operator = ( '+' => sub { my $i = 0; $i += $_ for @_; $i; }, '-' => sub { my $i = shift @_; @_ or return -$i; $i -= $_ for @_; +$i; }, '*' => sub { my $i = 1; $i *= $_ for @_; $i; }, '/' => sub { my $i = shift @_; @_ or return 1/$i; $i /= $_ for @_; + $i; }, ); my $ops = join '' => keys %operator; my $task = "( + 3 4 ( * 2 7 ( + 1 1 ) ) ( / 6 2 ) )"; 1 while $task =~ s/\(\s*([$ops])((?:\s*\d+)+)\s*\)/ local $_ = $2; $operator{$1}->(split); /e; print $task;

    Cheers,
    Ovid

    New address of my CGI Course.

Re: Solving lisp-style terms
by merlyn (Sage) on Jun 05, 2005 at 12:50 UTC
Re: Solving lisp-style terms
by diotalevi (Canon) on Jun 05, 2005 at 15:23 UTC

    *cough*

    use Data::Postponed 'postpone'; $_ = postpone( 1 ); $_ += 1; $_ *= 7; $_ *= 2; $_ += 3; $_ += 4; $_ += postpone( 6 ) / 2; # so the (/ 6 2) isn't flattened out. # Produces some lisp-like output $_->Dump; # (+ (+ (+ (* (* (+ 1 1) 7 ) 2) 3) 4) (/ 6 2) )
Re: Solving lisp-style terms
by ambrus (Abbot) on Jun 05, 2005 at 12:28 UTC

    I like this code.

    Here are some improvements on the code. The most important is that it can handle negation of numbers, such as (- (+ 1 1)) which evaluates to -2. This version uses the first cmd-line argument as the expression. ;Updates: you notice the lack of spaces, don't you?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://463700]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (2)
As of 2024-04-20 04:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found