I need to modify expressions that such as f[w, w, a, y, x, a, x, a]
into f[w1, w2, a1, y, x1, a2, x2, a3]
.
In short, I have expressions like head[args__]
, and I want to modify args
in such a way so as to have the arguments that appear multiple times in the argument list enumerate themselves sequentially and return the modified expression.
My code so far feels a bit clunky but it works on the test cases I considered relevant.
Here are some examples:
h[o,i,o,s,d] -> h[o1,i,o2,s,d]
h[o,i,s,o,d] -> h[o1,i,s,o2,d]
h[o,i,s,d,o] -> h[o1,i,s,d,o2]
h[i,o,s,o,d] -> h[i,o1,s,o2,d]
h[i,o,s,d,o] -> h[i,o1,s,d,o2]
h[i,s,o,d,o] -> h[i,s,o1,d,o2]
h[o,i,o,s,o,d] -> h[o1,i,o2,s,o3,d]
h[o,i,o,s,d,o] -> h[o1,i,o2,s,d,o3]
h[o,i,s,o,d,o] -> h[o1,i,s,o2,d,o3]
h[i,o,s,o,d,o] -> h[i,o1,s,o2,d,o3]
h[o,i,o,s,o,d,o] -> h[o1,i,o2,s,o3,d,o4]
And here's how the previous result was obtained:
{h[o, i, o, s, d], h[o, i, s, o, d], h[o, i, s, d, o], h[i, o, s, o, d], h[i, o, s, d, o],
h[i, s, o, d, o], h[o, i, o, s, o, d], h[o, i, o, s, d, o], h[o, i, s, o, d, o],
h[i, o, s, o, d, o], h[o, i, o, s, o, d, o]} /. h[args__] :> h @@ enumArgs[args]
Is there a better way to achieve the same result?
An extension I'm considering is
SetAttributes[modArgs, HoldAll]
modArgs[h_[args__]] := h @@ enumArgs[args]
without having to rely on ReplaceAll
, but I was wondering if enumArgs
— or something similar — can be coded otherwise.
Things I think could use some improvement:
enumArg
makes use ofToString
andSymbol
. In the context, I'm usingenumArgs
there doesn't seem to be any obvious way for things to go wrong, since I'm making sure I'm using atomic arguments for the heads and arguments of heads. But that may not always be the case.enumArg
andenumArgs
to some extent feel more procedural than I'd be comfortable with.enumArgs
plows through the argument list and does what it's supposed to do. I think it should be more 'discrete'.- Also in
enumArgs
, using the magic inPart
to perform in-place assignments is a fast way to do what you need to do, but feels a bit sloppy.
Would you agree with some/any of the points I raise above? Do you think it could have been done better?
If you disagree or think otherwise, please let me know.
Code
Process single argument:
(* process a single argument *)
enumArg[argument_, count_, index_] := Module[{res, id = index},
If[
count > 1,
(* more than one instances *)
res = Symbol@StringJoin[ToString[argument], ToString[count - index + 1]];
id = index - 1,
(* single instance *)
res = argument
];
{res, id}
]
Process function arguments:
(* process arguments *)
enumArgs[args__] := Module[{tally, count, pos, junk, bag = {}, res, id, argument, index},
(* count individual instances of arguments *)
tally = Tally[{args}];
count = tally[[All, -1]];
(
{{pos, junk}} = Position[tally, #];
{argument, index} = tally[[pos]];
{res, id} = enumArg[argument, count[[pos]], index];
bag = Flatten[{bag, res}];
tally[[pos, -1]] = id;
) & /@ {args};
bag
]