Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I would like to have a compact notation for specifying conditions and results in transformation rules.

Consider ReplaceAll:

r=ReplaceAll;

r[1, x_/;x>0:>-x]
 (* -1 *)

I'd like to avoid writing x_/; and :>. Here is what I came up with:

Clear[f];
Attributes[f] = {HoldRest};
f[val_,cond_,res_] := val /. x_ /; cond :> res

I could only get it to work thus,

f[1, x$>0, -x$]
 (* -1 *)

That expression may not even be robust, because I'm not sure that the pattern always gets renamed x$. But what I'm really after is a notation like this:

f[1, x>0, -x]

without the $ on the x. It should work even if x is defined (hence the HoldRest Attribute): for this purpose x should be interpreted simply as a notational convenience.

How can we do this?

My motivation is now one of curiosity, to get a better understanding of how scoping works inside patterns. Initially I was setting up conditions on a problem, for which many different replacement rules were to be used on the same expression, and I was looking for ways to simplify the notation so as not to keep writing /., x_/;, and :>.

share|improve this question
2  
Actually, in this particular case, renaming does the right thing for you, making the rule invariant over the pattern variable. Usually, fighting the renaming mechanism is a last resort, useful in some narrow set of cases (usually related to run-time code generation). Often, the need to do it indicates a bad design. You can defeat renaming, but what you get here is a broken function, which only works if you supply literally "x" (and not any other variable). To me, this new function f looks like an invitation for trouble. –  Leonid Shifrin Mar 3 '12 at 17:54
    
A recent vote reminded me of this question. As a matter of personal curiosity, why did you Accept Rojo's answer over mine? Using # still seems a more natural syntax to me, but perhaps I've overlooked something? –  Mr.Wizard Dec 21 '12 at 23:46
    
@Leonid since you always bring a deeper perspective: what do you think of my proposal? –  Mr.Wizard Dec 21 '12 at 23:47
    
@Mr Rojo provided considerable insight into the scoping issue in this context, which satisfied my personal curiosity. ;) I do agree that the slot mechanism is a natural choice in general. –  JxB Dec 22 '12 at 3:18
    
@Mr.Wizard It is probably ok to do this, but I personally would not probably do this because it may be confusing - by looking at such code (after a while or written by someone else), I would most definitely think that the ampersand was forgotten. Your suggestion is more along the macro-writing ideology, but then I'd write macros consistently. Mathematica does not currently provide the right support for them, although in principle it can be implemented from within Mathematica. Something I was also thinking about :) –  Leonid Shifrin Dec 22 '12 at 15:14

3 Answers 3

up vote 7 down vote accepted

I am not an expert on scoping constructs... Replacement rules aren't very respectful of inner scoping constructs, of the expression in which they are replacing. They seem to be respectful however of the scoping constructs of the expression they are building.

So, for example

Hold[val /. x_ /; cond :> res] /. cond :> x

returns

Hold[val /. x_ /; x :> res]

but, like your example

{1, x>0, -x}/.{val_,cond_,res_}:>(val/. x_/;cond:>res)

returns

1/. x$_/;x>0:>-x

In any case, we need to avoid the rescoping. Many ways to do this, but one would be to simply prevent the replacer to see your x_ as a scoped variable when it does the replacing

Attributes[f] = {HoldRest};
f[val_, cond_, res_] := val /. Pattern @@ Hold[x, _] /; cond :> res

In place of Pattern @@ Hold[x, _] you can put anything that evaluates to x_ without having it explicitly. You coudl make your own function, write Pattern[x, Sequence[], _], Union@Pattern[x, _, _], Identity[Pattern][x, _], Evaluate[Pattern][x, _], Reverse[Pattern[_, x]], ToExpression["x_"], Pattern @@ (x _) (notice the space, could be a +). In those cases that don't hold its arguments, however, you would need to add an Unevaluated to avoid problems if x is defined

@WReach's good suggestions in the comments are based on this too, on hiding the x_ as a scoped variable when the replacement is done, by injecting them later

Edit

Things like Pattern[x, 1 _^1+0] or stuff like that with the same structure (head Pattern2 arguments, won't work because it recognises it as a pattern)

Ok, I said there are many ways, so I'll give another example... Another way to implement the above is to lexically scope your Pattern so it isn't a pattern but you can type it as such. It probably only makes sense if your rhs is big and the lhs is small, but it also has the advantage of working when the pattern is inside held constructs. By the way, I never saw this done so I reserve the right to be suggesting something stupid and abstruse. In any case, its instructive and you wanted to learn, hehe

Attributes[f] = {HoldRest};
With[{rp = Pattern}, Module[{Pattern},
  f[rp[val, _], rp[cond, _], rp[res, _]] := Unevaluated@
     (val /. x_ /; cond :> res)
    /. Pattern :> rp]]

So, when you write your code, you can use x_ as you wish, because it will be interpreted as some Pattern$ASk that's not a scoping contruct. You use rp for those that you wish to become real patterns at definition time and _ those who you want to turn into patterns at execution time.

Another idea is, instead of hiding the scoping variable, hide the scoping constructs until runtime

Attributes[f] = {HoldRest};
With[{Condition = Hold[Condition], RuleDelayed = Hold[RuleDelayed], 
  ReplaceAll = Hold[ReplaceAll]},
 SetDelayed @@ 
  Hold[f[val_, cond_, res_], ReleaseHold[val /. x_ /; cond :> res]]
 ]

In order for the With to work you need to do something like that SetDelayed@@ because that's another scoping construct that With won't go into willingly. So, in this example, you see two layers of the trick.

share|improve this answer
    
This is great, thanks! –  JxB Mar 3 '12 at 6:03
1  
A couple more alternatives along the suggested lines: f[val_,cond_,res_] := With[{x = x_}, val /. x /; cond :> res] or f[val_,cond_,res_] := val /. # /; cond :> res &@ x_. –  WReach Dec 21 '12 at 23:28

This works, but it is not elegant:

Clear[f1];
Attributes[f1] = {HoldRest};
f1[val_, cond_, res_] := val /. y_ /; (Block[{x}, cond /. x :> #1] &)[
 y] :> (Block[{x}, res /. x :> #1] &)[y]

f1[1, x > 0, -x]
 (* -1 *)

Scaling this to many pattern variables is not pretty.

share|improve this answer

How about using Slot (#)?

SetAttributes[f, HoldRest]

f[expr_, cond_, rep_] := expr /. x_?(cond &) :> (rep &)[x]

f[{0, 1, 2}, # > 0, -#]
{0, -1, -2}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.