Why don't people like lisp?

Andrew Dalke adalke at mindspring.com
Wed Oct 22 03:12:16 EDT 2003


Erann Ga
> Here's a quick and dirty version:
>
> (defvar *operations* '(+ - * / **))
>
> (defun ** (n e) (expt n e))
>
> (defun rpn-compile (expr)
>   (let ( stack free-vars )
>     (dolist (term expr)
>       (cond ( (numberp term) (push term stack) )
>             ( (member term *operations*)
>               (push (list term (pop stack) (pop stack)) stack)
>               (rotatef (second (first stack)) (third (first stack))) )
>             (t (push term stack)
>                (pushnew term free-vars))))
>     (unless (= (length stack) 1) (error "Syntax error"))
>     (eval `(lambda (&key , at free-vars) ,(first stack)))))
>
>
> ? (funcall (rpn-compile '(0 b - b 2 ** 4 a c * * - 0.5 ** + 2 a * /))
>            :a 1 :b 6 :c 3)
> -0.5505102572168221
> ?

You know, I messed up a bit in the explanation in several
ways.  First, I wanted to show that Python code could directly
manipulate the AST and generate usable code.  I then chose
a problem which could be done that way and implemented it
in the canonical tokenizer/parser/translater/compiler framework
Kaz Kylheku (to whom I was responding) wanted, when it's
inappropriate for  this case, hence complicating my original code.

If I were actually asked to solve that problem I would have
done it like you did

import re
_name_re = re.compile("[A-Za-z_][A-Za-z_0-9]*$")
_operators = "* + - / **".split()
def compile(s):
    stack = []
    names = []
    for token in s.split():
        try:
            stack.append(float(token))
            continue
        except ValueError:
            pass
        if _name_re.match(token):
            names.append(token)
            stack.append(token)
        elif token in _operators:
            s = "(%s)%s(%s)" % (stack[-2], token, stack[-1])
            stack[-2:] = [s]
        else:
            raise AssertionError(token)
    assert len(stack) == 1
    s = "def RPN(" + ",".join(names) + "):\n\treturn "
    s += str(stack[0])
    exec s in {"__builtins__": {}}
    return d["RPN"]

That is, just build up then exec a string to get the function.

Second, I didn't mention until the end that I expected the
Lisp code to report the location (line numbers and even
character positions) of where an error occured in the RPN.
The code you have, and this quick approach, don't attempt
to handle error numbers, making it much more succinct.

Third, I didn't stress the requirement to identify illegal
inputs, like 'a(', which are accepted by your code up
until the eval step.  (I could have made it worse for
all of us if I allowed the characters '()#; in an identifier ;)

OTOH, the quick&dirty solution I just now did
doesn't allow Python keywords as variable names,
while my AST approach does.  That could be solved
with a bit of variable munging, but complicates things.

Eg, I changed my re pattern (in my AST version) to
accept anything other than a number or operator as a
keyword.  The following then works

    f = compile("(#) ');; try + -")
    # can't call the keyword arguments directly since they
    # aren't legal Python names.
    print f(**{"(#)": 5, "');;": 7, "try": 3})

and gives -5.  Our eval/exec approaches just could not
handle that case.

What does your code do for my example above?  ;)

                    Andrew
                    dalke at dalkescientific.com






More information about the Python-list mailing list