quiz about symbolic manipulation
Bengt Richter
bokr at oz.net
Sat Dec 14 01:44:46 EST 2002
On 14 Dec 2002 05:43:05 GMT, bokr at oz.net (Bengt Richter) wrote:
>On 14 Dec 2002 00:05:19 GMT, bokr at oz.net (Bengt Richter) wrote:
>[ ... first version substitute routine ...]
>> 'square(square(x+y)+z)+square(x+w)'
>> >>> substitute(e, 'square','(%s)**2')
>> '((x+y)**2+z)**2+(x+w)**2'
>>
>>Well, this is not so exciting for functions with multiple parameters. How
>>might you want to rewrite those? Probably isolate terms and rewrite each term
>>and then feed that as a tuple to a rewfmt? If you can assume no terms containing
>>nested commas (probably reasonable for math expressions) then it shouldn't be
>>too hard. But much beyond that and you may want to use the Python parser after all ;-)
>>
>>Anyway, I'll leave the rest as an exercise ;-)
>
>It was such a jellybean project I couldn't resist modding it a it to handle
>comma separated function args, and multiple functions:
>
One more time, allowing rewrite specs in terms of function parameters
specified as $1, $2, etc in the rewrite format.
====< simiosub.py >=======================================
# simiosub.py
# a program to rewrite function calls in an expression
from __future__ import generators
import tokenize, StringIO
def tokens(expr):
tokens = tokenize.generate_tokens(StringIO.StringIO(expr).readline)
return [t[1] for t in tokens]
def substitute(expr, **funs):
def funrew(expr, funs):
if isinstance(expr, str): toks = tokens(expr)
else: toks = expr
while toks:
if toks[0] not in funs:
yield toks.pop(0)
continue
currfun = toks.pop(0)
# find and process parenthesized call parameter expression
i = ump = 0
for t in toks:
i += 1
if t =='(': ump += 1
if t == ')':
ump -= 1
if not ump: break
rest = toks[i:]
parenexp = toks[1:i-1]
ixcommas = [t[1] for t in zip(parenexp,range(len(parenexp)))
if t[0]==',']+[len(parenexp)]
i = j = 0; paramdir= {}
for k in ixcommas:
i += 1 # for $1, $2, etc
paramdir[str(i)] = ''.join(funrew(parenexp[j:k], funs))
j = k+1
yield (funs[currfun] % paramdir)
toks = rest
# change $x to %(x)s in formats
sfuns = {}
for k, v in funs.items():
sl = v.split('$')
for i in range(1,len(sl)): sl[i] = '%%(%s)s%s' % (sl[i][0],sl[i][1:])
sfuns[k] = ''.join(sl)
return ''.join(funrew(expr, sfuns))
if __name__ == '__main__':
import sys
args = sys.argv[1:]
if not args: args = ['square(square(x+y)+z)+square(x+w)', 'square=($1)**2']
# should return '((x+y)**2+z)**2+(x+w)**2'
e = args.pop(0); funs = dict(map(lambda x: tuple(x.split('=')),args))
print 'before:', e
for sub in args: print ' ',sub
print ' after:', substitute(e, **funs)
==========================================================
Default test:
[22:42] C:\pywk\clp>simiosub.py
before: square(square(x+y)+z)+square(x+w)
square=($1)**2
after: ((x+y)**2+z)**2+(x+w)**2
A little more complex, changing to infix function calls:
[22:42] C:\pywk\clp>simiosub.py foo(x*y)+zeep(1,foo(2),3) "foo=(foo $1)" "zeep=(zeep $1 $2 $3)"
before: foo(x*y)+zeep(1,foo(2),3)
foo=(foo $1)
zeep=(zeep $1 $2 $3)
after: (foo x*y)+(zeep 1 (foo 2) 3)
Using the reusability of parameters in the rewrite format:
[22:42] C:\pywk\clp>simiosub.py square(x+y) "square=($1)*($1)"
before: square(x+y)
square=($1)*($1)
after: (x+y)*(x+y)
And on the original test:
[22:45] C:\pywk\clp>simiosub.py square(square(x+y)+z)+square(x+w) "square=($1)*($1)"
before: square(square(x+y)+z)+square(x+w)
square=($1)*($1)
after: ((x+y)*(x+y)+z)*((x+y)*(x+y)+z)+(x+w)*(x+w)
Kind of fun, but that's enough following myself up ;-)
Regards,
Bengt Richter
More information about the Python-list
mailing list