TransWikia.com

Modified "Plus" function: how to save just the final result of an addition of multiple objects?

Mathematica Asked on October 22, 2021

In my version of object-oriented programming in Mathematica, essentially (without going into details) obj[a] is a reference to an object whose actual data is stored inside data[a]. Methods like obj[a][function] then use and/or modify that data.

Schematically, I define addition of objects using UpValues as follows:

obj[a_] + obj[b_] ^:= add[a, b];

The add function looks at the values stored in data[a] and data[b], combines them in the desired way, and then stores them in data[ui] where ui is a new unique identifier (with i an integer) and returns obj[ui].

Now, here is my problem. Consider a long chain of additions, e.g.

obj[a] + obj[b] + obj[c] + obj[d]

This creates 3 new objects in total, obj[u1], obj[u2], and obj[u3], since each addition of two objects creates a new object. Schematically:

obj[a] + obj[b] + obj[c] + obj[d]
= obj[u1] + obj[c] + obj[d]
= obj[u1] + obj[u2]
= obj[u3]

If many objects are added, this creates an abundance of new objects that I don’t need. After the addition is over, I would like to clear all of them except the last one, which is the actual result of the expression.

Just clearing all of the obj[ui] for i less than the current index every time I add two objects won’t work, for two reasons:

  1. Older intermediate steps are still in general needed for the next steps. For example, In the addition I showed above, if the addition obj[c] + obj[d], which results in obj[u2], clears obj[u1], then I won’t be able to add obj[u1] + obj[u2] to produce obj[u3].
  2. If I perform another (separate) addition later, then I don’t want it to delete the results of the previous addition.

Therefore, I need to somehow know where the instance of add[a, b] that is currently running is located in the chain: is it the first, the last, or an intermediate step. If it’s the first, I mark the current i as the beginning of the objects to delete. If it’s the last, then I delete all of the objects created from that point until i - 1 where i is the current step.

A simpler option is just to know that the instance is the last step, and then rename the object to something like obj[vi] where vi is a new unique identifier and delete all of the obj[ui].

Is that possible? If not, what are some other ways in which I can avoid keeping all the objects that were created in the intermediate steps?

EDIT 1:

I am also doing something similar with multiplication, i.e. obj[a_] * obj[b_] ^:= multiply[a, b], so the solution will need to incorporate combinations like obj[a] + obj[b]*obj[c], which significantly complicates things.

EDIT 2:

The solution I am currently considering is to have the user enclose any operation with some kind of wrapper (e.g. cleanup[ obj[a] + obj[b]*obj[c] ]) and have cleanup[] rename its argument to obj[vi] and delete all the existing intermediate steps obj[ui]. This makes the syntax slightly less elegant for the user, but doesn’t require any complicated pattern matching.

It would be nice if there was some way to have the wrapper execute automatically when a cell ends its evaluation, or something like that, in which case the user won’t have to worry about the wrapper whenever they make an addition/multiplication operation.

One Answer

Maybe something along these lines?

obj /: Plus[args : obj[_] ..] := add @@ {args}[[All, 1]];
obj[a] + obj[b] + obj[c]

add[a, b, c]

This way you can write your own multi-argument version of add that encapsulates all of the internal juggling with objects and then cleans up at the end.

Alternatively, if you want to just apply add repeatedly to pairs of arguments:

obj /: Plus[args : obj[_] ..] := Fold[add, {args}[[All, 1]]];
obj[a] + obj[b] + obj[c]

add[add[a, b], c]

Edit

About your question about mixing Times and Plus: You can make a pattern like Plus[args1___, x : obj[_], args2___] (and similarly for Times) to interrupt normal evaluation the moment an object is found as an argument for one of these functions. You can then pull out the arguments and inspect them further (see if args1 has Times[___] in it, for example). However, if you have something like:

Plus[Times[obj[a], obj[b]], Times[obj[c], obj[d]]]

there is no way you can interrupt at the level of Plus with UpValues because those can only "pop up" one level. In that case, one of the Times expressions will first have to resolve to obj[_] before the Plus upvalue can fire.

Edit 2

Your suggestion of a cleanup wrapper could be done like this as well. For example, you could do:

Plus[args1___, x : obj[_], args2___] /; !TrueQ[wrappedQ] := Block[{
  wrappedQ = True
},
   cleanup[Plus[args1, x, args2]
]

The wrappedQ variable prevents recursion of this definition. This way you don't need to manually enforce the use of the cleanup wrapper.

Answered by Sjoerd Smit on October 22, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP