I would like to be able to have a pattern that matches only expressions that are (alternately: are not) children of certain other elements.
For example, a pattern to match all List
s not within a Graphics
object:
{ {1,2,3}, Graphics[Line[{{1,2},{3,4}}]] }
This pattern would match {1,2,3}
but not {{1,2},{3,4}}
.
There are relatively easy ways to extract expressions matching these criteria, but patterns are not only for extraction, but also for replacement, which is my main use case here (ReplaceAll
).
Do you know of any easy, concise, and general ways to do this?
Is it possible to do this at all with just patterns?
I am probably misunderstanding you, but, if I do understand correctly you want to match all expressions with head
List
which have the property that, going upwards in the expression tree, we'll never meet aGraphics
. I m not sure how to do this in one pass, but if you are willing to match twice, you can do something likewhich first selects elements so that the
Head
isn'tGraphics
(this is done byCases[#, Except@Graphics[___]] &
, which returns{randhead[5], {1, 2, {3, 5}}}
), then selects those withHead
List
from the returned list. Note that I've added some more stuff tolst
.But presumably you knew this and were after a single pattern to do the job?
According to your explanation in the comment to the acl's answer:
I think it could be done in one pass with
ReplaceAll
. We can rely here on the documented feature of theReplaceAll
: it does not look at the parts of the original expression which were already replaced even if they are replaced by themselves! Citing the Documentation: "ReplaceAll
looks at each part ofexpr
, tries all the rules on it, and then goes on to the next part ofexpr
. The first rule that applies to a particular part is used; no further rules are tried on that part, or on any of its subparts."Here is my solution (
whatIwant
is what you want to do with matched parts):Here is your test case:
Here is a function that replaces only inside certain head (
Graphics
in this example):Here is a test case:
I will propose a solution based on expression pre-processing and soft redefinitions of operations using rules, rather than rules themselves. Here is the code:
A few details on implementation ideas, and how it works. The idea is that, in order to restrict the pattern that should match, we may wrap this pattern in some head (say
h
), and also wrap all elements matching the original pattern but also being (or not being) within some other element (matching the "parent" pattern) in the same headh
. This can be done for generic "child" pattern. Technically, one thing that makes it possible is the intrusive nature of rule application (and function parameter-passing, which have the same semantics in this respect). This allows one to take the rule likex_List:>f[x]
, matched by generic patternlhs_:>rhs_
, and change it toh[x_List]:>f[x]
, generically by usingh[lhs]:>rhs
. This is non-trivial becauseRuleDelayed
is a scoping construct, and only the intrusiveness of anotherRuleDelayed
(or, function parameter-passing) allows us to do the necessary scope surgery. In a way, this is an example of constructive use of the same effect that leads to the leaky functional abstraction in Mathematica. Another technical detail here is the use ofUpValues
to overload functions that use rules (Cases
,ReplaceAll
, etc) in the "soft" way, without adding any rules to them. At the same time,UpValues
here allow the code to be universal - one code serves many functions that use patterns and rules. Finally, I am using theModule
variables as a mechanism for encapsulation, to hide the auxiliary headh
and functionpreprocess
. This is a generally very handy way to achieve encapsulation of both functions and data on the scale smaller than a package but larger than a single function.Here are some examples:
It is expected to work with most functions which have the format
fun[expr_,rule_,otherArgs___]
. In particular, those includeCases,DeleteCases, Replace, ReplaceAll,ReplaceRepeated
. I did not generalize to lists of rules, but this should be easy to do. It may not work properly in some subtle cases, e.g. with non-trivial heads and pattern-matching on heads.You might write a recursive function that descends an expression tree and acts on the types of expression you want only if inside the right type of sub-expression, while leaving everything else alone. Patterns would be used heavily in the definition of the function.
Consider, for example, the following expression.
Let's suppose that we want to rotate every ordered pair that we see in the first argument of
Graphics
about the origin through the angle Pi/4, while leaving other points alone. The following function does this.Now we check