Search code examples
argumentsmaximawxmaxima

(wx)Maxima: how to get consistent expressions using `args`?


I'm trying to write a small script that will look at the first term of an expression and determine whether it is positive or negative, then print a + or - in front of that expression, accordingly; however, I'm having a bit of trouble writing it in such a way that it reliably extracts the first term of the expression.

I have been experimenting with part and args. I have been leaning towards args because I haven't found any way to determine the "depth" of parts for an arbitrary expression (i.e. I'm not sure how one can determine whether to use, e.g. part(expr,1) or part(expr,1,1) or part(expr, 1,1,1) etc.).

The issue with args is that, e.g.

declare(cos, posfun)$
args(-2*cos(x));
    > [2 cos(x)]

i.e. the negative is dropped, presumably due to the lisp representation of the expression (we get the same result from part(-2*cos(x),1); moreover, part(-2*cos(x),2) "falls off the end" -- it seems part simply can't see the -).

By contrast,

args(-2*cos(x)+x);
    > [x, -2cos(x) ]

as expected.

Regardless of whether or not this is the desired behaviour for these functions, I was hoping to find some way to get around it so that I can have a function that would have the following bevaiour:

addOp(x) > ["+", x]
addOp(-x) > ["-", x]

addOp(1+2*x+x^2) > ["+", 1+2*x+x^2]
addOp(-2+2*x+x^2) > ["-", 2+2*x+x^2] /* NB: only the first term is scaled by -1, not the entire expression */

addOp(cos(...)) > ["+", cos(...)]
addOp(-2x*cos(...)) > ["-", 2x*cos(x) ]

I also tried using the op function along with a known number; however, the internal representation of negative numbers means that something like op(1-3*cos(x)) returns +.

This one has had me stumped for a while, so any suggestions would be greatly appreciated.


Solution

  • Here's my first try. It seems to mostly work the way you describe except for %o11 because the -2 gets moved from the beginning to the end.

    (%i1) f(e):= if atom(e) then ["+", e]
     else if op(e) = "-" then ["-", -e]
     elseif op(e) = "+" then [f(first(e)), rest(e)]
     else ["+", e];
    (%o1) f(e) := if atom(e) then ["+", e] else (if op(e) = "-" then ["-", - e]
                      elseif op(e) = "+" then [f(first(e)), rest(e)] else ["+", e])
    (%i2) f(x);
    (%o2)                               [+, x]
    (%i3) f(-x);
    (%o3)                               [-, x]
    (%i4) f(-2*x);
    (%o4)                              [-, 2 x]
    (%i5) f(-2*cos(x));
    (%o5)                            [-, 2 cos(x)]
    (%i6) f(1-2*cos(x));
    (%o6)                        [[+, 1], - 2 cos(x)]
    (%i7) f(-1+2*cos(x));
    (%o7)                        [[+, 2 cos(x)], - 1]
    (%i8) f(-1-2*cos(x));
    (%o8)                        [[-, 2 cos(x)], - 1]
    (%i9) f(a*b+c*d-e*f*g);
    (%o9)                       [[-, e f g], c d + a b]
    (%i10) f(1+2*x+x^2);
                                        2
    (%o10)                        [[+, x ], 2 x + 1]
    (%i11) f(-2+2*x+x^2);
                                        2
    (%o11)                        [[+, x ], 2 x - 2]
    (%i12) f(cos(a*b-c));
    (%o12)                         [+, cos(c - a b)]
    (%i13) f(-2*cos(x-y*z));
    (%o13)                        [-, 2 cos(y z - x)]
    (%i14) f(-2*x*cos(b-c));
    (%o14)                        [-, 2 cos(c - b) x]
    (%i15) -2+2*x+x^2;
                                      2
    (%o15)                           x  + 2 x - 2
    (%i16) f(-2 + 2*x - x^2);
                                        2
    (%o16)                        [[-, x ], 2 x - 2]
    (%i17) -2 + 2*x - x^2;
                                       2
    (%o17)                         (- x ) + 2 x - 2
    (%i18) f(a-b);
    (%o18)                           [[+, a], - b]
    (%i19) f(b-a);
    (%o19)                           [[+, b], - a]
    

    The business about op(e) = "-" is that stuff like -2*cos(x) gets reorganized into -(2*cos(x)) before args works on it (although I think inpart disables that behavior or modifies it).

    EDIT: Take 2. atom(-2) returns true, so -2 is caught by the first case in the previous definition. Here's another try, where negative numbers are distinguished from other atoms.

    f(e):= 
    if atom(e)
     then (if numberp(e) and e < 0 then ["-", -e] else ["+", e])
     else if op(e) = "-" then ["-", -e]
     elseif op(e) = "+" then [f(first(e)), rest(e)]
     else ["+", e];
    

    I didn't try this code but maybe you can say whether it works.