Let the following exprs
stand for a list of complicated expressions which we are interested in breaking down into parts by extracting a number of individual subcomponents, from each expression:
exprs = {f1["b", f2[1], head1["a", 14] f3["c", head2[12, 3], head1["b", 8]]] head2[2, 8], f2[5]};
The patterns we are interested in are of the following forms:
patts = {head1["a", x1_], head2[x2_,y_]};
Now, here comes what is troubling me:
The obvious way ( to me ) to extract the desired parts ( here
x1
,x2
andy
) is to useCases
:Cases[exprs, #->{x1, x2, y}, Infinity]& /@ patts
which in turn returns a list of the requested parts per expression
{{{14,x2,y}} ,{{x1,12,3}, {x1,2,8}}}
This way of getting the requested parts out is not productive in the sense that when having to extract 10 or 20 parts or more, providing Cases
with an exhaustive list is a no-go. I need something more automated.
One possible solution I could come up with was to use something like syms = Cases[patts, p_Pattern :> p[[1]], Infinity]
to extract beforehand all the symbols associated with the desired subexpressions, since the patterns are handcoded and are known before the extraction 'starts'. The amended Cases
and its return value are shown below
Cases[exprs, #->syms, Infinity]& /@ patts
{{{14,x2,y}}, {{x1,12,3}, {x1,2,8}}}
This is still not 100% usable because it would be nice if there were a way to postprocess the extracted subexpressions after they are identified.
One possible way to do that is
Cases[exprs, # -> postproc[syms], Infinity]& /@ patts
which evaluates to
{{postproc[{14,x2,y}]}, {postproc[{x1,12,3}], postproc[{x1,2,8}]}}
which seems to do the trick, but brakes down as soon as I try to define postproc
in a way I find useful, eg. as in the following sense.
keys=ToString /@ syms;
postproc[x_, h_: keys] := AssociationThread[h -> x]
{{<|"x1"->x1,"x2"->x2,"y"->y|>}, {<|"x1"->x1,"x2"->x2,"y"->y|>, |<|"x1"->x1,"x2"->x2,"y"->y|>}}
As anyone can see, as soon as postproc
gets a definition the output is not evaluated in the desired way.
I've tried several iterations for the definition of postproc
along with interchanging Rule
with RuleDelayed
in the second argument of Cases
. The only thing I could come up that works is Hold
ing the head of postproc
.
Cases[exprs, #->Hold[postproc][syms],Infinity] & /@ patts // ReleaseHold
{{<|"x1"->14,"x2"->x2,"y"->y|>}, {<|"x1"->x1,"x2"->12,"y"->3|>, <|"x1"->x1,"x2"->2,"y"->8|>}}
OK, so this works but I don't know if there's a better/more intuitive/more paradigmatic way to code something like this.
Please help, any suggestion is appreciated.