7
$\begingroup$

This is for the purpose of generating a string (actually it will Latex string, but that is not important).

I need to generate string as

        "z " <> ToString[InputForm[expression]]

Which is meant to add/combine the two expressions (not mathematically add them, but just to format them on the line, as part of equation). This is part of of the RHS side of an equation.

The thing is, expression is an input argument which I do not know what the front sign on will be. So if the function writes

        "z  + " <> expression

And the expression happened to be -x then it will come out as z + - x. And if the function instead does

        "z  " <> expression

And input is just x then it will come out as z x which is wrong, as it look like multiplication. It should be only a + or - between them.

Getting the sign turned out to be more tricky than I thought, since the input can be any mathematical expression and FullForm is needed to figure the sign.

So by trial and error, I think I got all the cases covered in the function below, which takes expression and tried to figure if it should add + or not to it.

My question is, is there a better way to do this? I am sure there is, as it seems too much work for something like this. (some might not like the use of Return, but if I do not do this, there will be too long if/then/else chain, and Which does not help much here, any way, the style can be changed).

exprWithSign[expr_] := Module[{e, e0, pos, neg},
  pos = " + " <> ToString[InputForm[expr]];
  neg = ToString[InputForm[expr]];

  If[Head[expr] === Symbol, Return[pos, Module]];

  If[ Head[expr] === Integer || Head[expr] === Rational,
   If[Sign[expr] == -1,
    Return[neg, Module]
    ,
    Return[pos, Module]]
   ];

  e = First@expr; 
  If[(Head[e] === Integer || Head[e] === Rational),
   If[Sign[e] == -1,
    Return[neg, Module]
    ,
    Return[pos, Module]
    ]
   ];

  If[Head[e] === Times,
   e0 = First@e; 
   If[(Head[e0] === Integer || Head[e0] === Rational),
    If[Sign[e0] == -1,
     Return[neg, Module]
     ,
     Return[pos, Module]
     ]
    ]
   ];

  Return[pos, Module]      
  ]

To test the above:

Clear[x,y]
testCases={-1/2,
           1/2,
           1,
          -1,
           x,
           x-y,
          -x-y,
          2/3-x,
          -2 x+1,
          Sin[x],
         -Sin[y]+Sin[x],
         4 Sin[x]/(-3)}; (*last one is tricky*)

Print["z " <> exprWithSign[#]]&/@testCases

Mathematica graphics

$\endgroup$
  • 4
    $\begingroup$ You might be interested in Internal`SyntacticNegativeQ[]: Internal`SyntacticNegativeQ /@ {-5, -1/Sqrt[3], -x, Pi, y - 1} $\endgroup$ – J. M. is away Aug 3 '17 at 3:10
  • $\begingroup$ @J.M. Thanks! I never seen this internal function before. If you like to post this as answer will be happy to accept it unless this is a duplicate question. $\endgroup$ – Nasser Aug 3 '17 at 3:22
  • $\begingroup$ I won't be able to write a proper answer until much later, but if you've figured it out already, consider writing a self-answer instead. $\endgroup$ – J. M. is away Aug 3 '17 at 3:24
  • $\begingroup$ @J.M.'s comment is probably the way to go. Alternatively, if your final code works recursively, you may first generate the converted expression, then see if the first symbol in the string is "-", and then write the "+" only if there was no minus. $\endgroup$ – JEM_Mosig Aug 3 '17 at 3:49
  • $\begingroup$ @JEM_Mosig This is tricky. First I use TeXForm and then convert to string. So "-" in the string might not be the first letter due to TeX strange formatting. But even if I use InputForm to check, try this one str=ToString[InputForm[4 Sin[x]/(-3)]] this gives the string "(-4*Sin[x])/3" so you see the first letter is "(" even though the sign is "-". So I can't just check for the first letter in the string. I switched to using Internal`SyntacticNegativeQ now. (too many Internal functions, too little time :) $\endgroup$ – Nasser Aug 3 '17 at 4:01
3
$\begingroup$

Why do you need to use both TeXForm and InputForm? If you're interested in ending up with a latex string, perhaps you could use something like:

Replace[
    testCases,
    Verbatim[Plus][a__] | a__ :> ToString[HoldForm[Plus[z,a]], TeXForm],
    {1}
]

${z-\frac{1}{2},z+\frac{1}{2},z+1,z-1,z+x,z+x-y,z-x-y,z+\frac{2}{3}-x,z+1-2 x,z+\sin (x),z+\sin (x)-\sin (y),z-\frac{4 \sin (x)}{3}}$

$\endgroup$
  • $\begingroup$ Thanks. btw, I do not use both InputForm and TeXForm. I use TeXForm only. for the output. I just thought it will be simpler to use InputForm in the question, since the logic of finding the sign should not depend on which form one uses. Your solution works very well. Will test it more with my main report which has thousands of equations to format. $\endgroup$ – Nasser Aug 3 '17 at 6:43
3
$\begingroup$

Maybe this is too simple to handle all the real cases, but it seems to work on your test cases.

exprWithSign[expr_] :=
  StringReplace[" + " <> ToString[InputForm[expr]], "+ -" -> "- "]    

Clear[x, y]
testCases = 
  {-1/2, 1/2, 1, -1, x, x - y, -x - y, 2/3 - x, -2 x + 1, Sin[x], -Sin[y] + Sin[x]};
Print["z " <> exprWithSign[#]] & /@ testCases;

test

$\endgroup$
  • $\begingroup$ Thanks for the effort, but as I said in the comment, this fails on something like 4 Sin[x]/(-3) as it returns z + (-4*Sin[x])/3 when I'd like to format this z - (4*Sin[x])/3 as it looks more natural. This test case just came up in comments. Will add it now to my question so it is there more clearly. $\endgroup$ – Nasser Aug 3 '17 at 5:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.