Skip to main content

How to do Cases with multiple related patterns?



I would like to write a function similar to Cases, but can search for a group of related patterns together. For example,


casesList[f[x] g[x] + f[y] + g[y]/f[z], {f[a_], g[a_]}]


{{f[x], g[x]}, {f[y], g[y]}}



By "related patterns", I mean that in the above example a_ matches the same x for both f and g at the first list, and matches y at the second list.


Note that here the input {f[a_], g[a_]} could be any other patterns. As another example,


caseList[D[f[x,y],x] + D[f[x,y],y] + D[g[x,y],x] + D[g[x,y],y],
{D[f_[x_,y_],x_], D[f_[x_,y_],y_]}]



{{Derivative[1, 0][f][x, y], Derivative[0, 1][f][x, y]}, {Derivative[1, 0][g][x, y], Derivative[0, 1][g][x, y]}}



Is there a simple way to do this? Thanks!


EDIT: To further clarify the question, I'd like to compare the situation with Cases for a list. For example,


Cases[{{f[a], f[b]}, {f[c], f[c]}}, {f[a_], f[a_]}, Infinity]


{{f[c], f[c]}}




Cases[{{f[c], f[c]}}, {f[a_], f[b_]}, Infinity]


{{f[c], f[c]}}



In the above two examples, Cases does exactly what I want. However, more generally the expressions which match f[a_] does not necessarily stays in a list structure, but rather may be at elsewhere in the expression. This is the major difficulty I met.



Answer



I'm not entirely sure whether this is right and works correctly, but the following could be an idea for a general rule-based approach. The idea is to use Cases to extract all matching expression separately for the given list of patterns. Let me illustrate this by your simple f example


expr = f[x] g[x] + f[y] + g[y]/f[z];


Cases[expr, #, Infinity, Heads -> True] & /@ {f[a_], g[a_]}
(* {{f[y], f[x], f[z]}, {g[x], g[y]}} *)

Now we have two result lists where all in the first list match f[a_] and all in the second list match g[a_]. Having this, the next step is kind of obvious: We need a replacement rule, where the a_ will match the same in both patterns. Given our result, this should be an easy rule of the following form


{{___,f[a_],___},{___,g[a_],___}} :> {f[a], g[a]}

The only tedious work is to build this rule from the input pattern list {f[a_], g[a_]}. Let's assume we have already build this, then we can use ReplaceList to get all possibilities


ReplaceList[{{f[y], f[x], f[z]}, {g[x], 
g[y]}}, {{___, f[a_], ___}, {___, g[a_], ___}} :> {f[a], g[a]}]

(* {{f[y], g[y]}, {f[x], g[x]}} *)

Looks OK for me. With this in mind, we can write our CasesList combining all ideas. Here you see how the last replacement rule is built automatically


CasesList[expr_, pattern_List] := 
With[{cases = Cases[expr, #, Infinity, Heads -> True] & /@ pattern,
ruleLHS = {___, #, ___} & /@ pattern,
ruleRHS = pattern /. Verbatim[Pattern][arg_, ___] :> arg
},
ReplaceList[cases, ruleLHS :> ruleRHS]
]


Now, let's try this with your second example


CasesList[D[f[x,y],x]+D[f[x,y],y]+D[g[x,y],x]+D[g[x,y],y],
{D[f_[x_,y_],x_],D[f_[x_,y_],y_]}]


{{Derivative[1, 0][f][x, y], Derivative[0, 1][f][x, y]}, {Derivative[1, 0][g][x, y], Derivative[0, 1][g][x, y]}}



Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...

list manipulation - Selecting multiple columns from a matrix?

Sample data: data = { {{2013, 1, 1}, 24.13, 167.67, 231.82}, {{2013, 1, 2}, 32.15, 170.92, 225.99}, {{2013, 1, 3}, 35.43, 172.68, 221.67}, {{2013, 1, 4}, 36.73, 173.05, 218.32}, {{2013, 1, 5}, 58.19, 165.96, 197.05}, {{2013, 1, 6}, 69.99, 163.50, 187.52}, {{2013, 1, 7}, 71.37, 154.21, 175.58}, {{2013, 1, 8}, 72.51, 149.66, 163.25}}; I want a DateListPlot with three graphs, so for a matrix formed by columns 1 and 2, one for columns 1 and 3, and 1 for columns 1 and 4. At the moment I'm using this code: data2 = Transpose[{data[[All, 1]], data[[All, 2]]}]; data3 = Transpose[{data[[All, 1]], data[[All, 3]]}]; data4 = Transpose[{data[[All, 1]], data[[All, 4]]}]; DateListPlot[{data2, data3, data4}, Joined -> True, Filling -> {3 -> {1}}] but I have a hunch that this can be done more efficiently. I don't like the Transpose s in particular. Any ideas? edit (for extra credit) What if I need to multiply the second column by 2, which in my solution is simp...