5
$\begingroup$

The built in Plus and Times operators when rendered behave the following way:

Plus[a,b,Times[-1,c],d]

a + b - c + d

Basically as I see Times forces Plus to omit the "+" character before "-c".

Question

How to define for my own two functions a notation which renders a form such as

op1[a, b, op2[c], d]

as

a op1 b op2[c] op1 d

$\endgroup$
4
  • $\begingroup$ Is there really nothing between b and c? $\endgroup$
    – Lukas Lang
    Commented May 10, 2018 at 10:17
  • $\begingroup$ @Mathe172 yes, as there is nothing between 'b' and '-c' $\endgroup$
    – Adam
    Commented May 10, 2018 at 10:23
  • $\begingroup$ But then inputting a op1 b c op1 d will be equivalent to op1[a,b*c,d], or am I missing something? Or should it not be possible to input anything using this notation? $\endgroup$
    – Lukas Lang
    Commented May 10, 2018 at 10:25
  • $\begingroup$ @Mathe172 true that, I only thought about formatting the output, edited the question $\endgroup$
    – Adam
    Commented May 10, 2018 at 11:02

2 Answers 2

4
$\begingroup$

The best way IMO is not to change anything in the way the expressions are stored, but only to change the way they are printed. We can do this by defining a small command, minus[a], which is simply printed with the extra minus sign. We can then switch each occurrence of Times[-1,a] with this command after the expressions are evaluated but before they are printed. We can do this with \$PrePrint command.

The code:

$PrePrint = # //. Times[-1, a_] :> minus[a] &;
Format[minus[a_]] := -a;

Example:

expression = a - b;
expression

(* a + -b *)

Since we are only effecting the expressions after they are evaluated, the command minus is actually not in the expressions, so they work as expected:

expression+b
(* a *)

Above simple code can be further generalized to ensure that any manifestly negative expression is printed as OP wanted, not just those of the form Times[-1,a]. To do this, we simply do as follows:

$PrePrint = # //. {
  Times[a___, b_?Negative, c___] :> minus[Abs@b, a, c], 
  Times[a___, Complex[0, b_?Negative], c___] :> minus[Complex[0, Abs@b], a, c]
  } //. Complex[0, b_?Negative] :> minus[Complex[0, Abs@b]] &;
Format[minus[a_, b___]] := -Times[a, b];

Now, we can get any expression with an explicit overall minus sign in the required printed form.

Examples:

expr = {a - b, a + b - 2 c, a - Pi, a - 3 I , a - I b}; expr

(* {a + -b, a + b + -2 c, a + -\[Pi], a + -3 I, a + -I b} *)

Note that the code is only about printing, so it does not interfere with evaluation. Hence, for example, for $a-2a$, you would get $-a$, not $a+-2a$! Also, if a numeric number is not purely real or purely imaginary, the code does not do anything; for example

exp = a - 1 + I; exp

(* (-1 + I) + a *)

The code did not change $(-1+I)$ to $-(1-I)$ as this numeric number is neither purely real nor purely imaginary!

$\endgroup$
3
$\begingroup$

Here is one possible solution:

Note: I'll be using / (respectively oplus/ominus) to recreate the effect of +/- - change operators/symbols as necessary

First, use InfixNotation to get the foundation:

InfixNotation[ParsedBoxWrapper["⊕"], oplus]
InfixNotation[ParsedBoxWrapper["⊖"], ominus]
Attributes[oplus] = {Flat, OneIdentity};
Attributes[ominus] = {Flat, OneIdentity};

This gets us to here:

a⊕b⊖c⊖c⊕d // FullForm
(* oplus[a,ominus[b,c,c],d] *)

Next, transform ominus[…] into something more similar to Plus[a,Times[-1,b]]:

ominus[a_, b__] := oplus[a, Sequence @@ Map[oinv, {b}]]

Giving us:

a⊕b⊖c⊖c⊕d // FullForm
(* oplus[a,b,oinv[c],oinv[c],d] *)

So input is already working. Output formatting still has some issues though:

oplus[a,b,oinv[c],oinv[c],d]
(* a⊕b⊕oinv[c]⊕oinv[c]⊕d *)

Basically, we need to replace ⊕oinv[…] with ⊖…. The following should achieve this (hopefully) robustly:

NotationMakeBoxes[oplus[arg1_, args__], StandardForm] /; MemberQ[$ContextPath, "Global`"]:=
  RowBox@Riffle[
   List @@ (
     FixedPoint[
         Replace[
          HoldComplete[pre___, unproc@bef_, Longest[inv : unproc@oinv[_] ..], post___] :>
              HoldComplete[pre, ominus[bef, inv], post]
          ],
         unproc /@ HoldComplete[arg1, args]
         ] //. HoldPattern@ominus[pre___, unproc@oinv[inv_], post___] :>
              ominus[pre, inv, post] 
           /. om_ominus :> MakeBoxes[om, StandardForm] 
           /. unproc@arg_ :> Parenthesize[arg, StandardForm, CirclePlus]
     ),
   "⊕"
   ]

First, lets verify that it works:

oplus[a,b,oinv[c],oinv[c],d]
(* a⊕b⊖c⊖c⊕d *)

How it works

The basic idea is to override the formatting rule put in place by InfixNotation and replace it with something that knows how to handle oinv[…]:

  • First wrap the args in HoldComplete and wrap all arguments in unproc, to keep track of which arguments need processing at the end.
  • Then, we replace argument sequences of the form unproc@arg1,unproc@oinv[arg2],unproc@oinv[arg3],… with ominus[arg1,unproc@oinv@arg2,unproc@oinv@arg3,…] (since we are within HoldComplete this is not reverted)
  • Then, we remove the unproc@oinv@ wrappers within all ominus
  • Then, we call the existing (thanks to InfixNotation) formatter for ominus
  • And finally, we format the remain unproc arguments using the default formatting routine
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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