2
$\begingroup$

I want to define a function that only operates on expressions with a head from a given list, such as

f[expr:(_And|_Or)] := Print["LHS is ", expr[[1]]];

This of course works fine when x is just an expression such as expr = p == q || p == q + 1. However, now I'd like to be able to print out parts of expr even when p and q both have values defined. That is, if f and expr are defined as above, then

p = 1;
q = 2;
f[expr]

should print "LHS is True". The obvious thing to try is of course to add

SetAttributes[f, HoldFirst]

but now f doesn't match expr, i.e. f[expr] simply returns f[expr]. I've tried variations of this, but nothing I can come up with seems to work, expr is always either evaluated before matching the head or the correct head is not found.

$\endgroup$

2 Answers 2

3
$\begingroup$

With more complex patterns, and especially held values, you have to think carefully about how your expression will be resolved.

a := ( Print[ "a!" ] ; True ) ;
SetAttributes[ f , HoldFirst ] ;
f[ x_ /; x == True ] := x ;
f[ a ]

a!

a!

True

Had a had been something like p == q and p and q had values, it would have been reduced to True or False each time, meaning you could not test for the Head. So why was it evaluated twice?

Unevaluated[ f[ x_ /; x == True ] := x ] // FullForm

Unevaluated[ SetDelayed[ f[ Condition[ Pattern[ x , Blank[ ] ] , Equal[ x , True ] ] ] , x ] ]

There are 2 copies of x not inside a Pattern: one in the condition, and one on the right-hand side. So the trick is to block Mathematica's normal evaluation routine to protect your symbolic expressions.

Controlling evaluation is a broad topic; MrWizard recently recommended this very helpful article on the subject to me, which I in turn recommend.

In this case you can achieve that with:

ClearAll[ f , p , q ] ;
heads = Alternatives @@ ( Blank /@ { And , Or } ) ;
SetAttributes[ f , HoldFirst ] ;
f[ x_ ] := List @@ Unevaluated @ x /; MatchQ[ Unevaluated @ x, heads ] ;
f[ p == q || p == q + 1 ]

{ p == q , p == 1 + q }

p = 2 ;
q = 1 ;
f[ p == q || p == q + 1 ]

{ False , True }

f[ 1 + 2 ]

f[ 1 + 2 ]

Depending on what exactly you're doing, it may be easier to approach the problem in this style, however:

ClearAll[ p , q ] ;
{ p == q , p == 1 + q } /. { p -> 2 , q -> 1 }

{ False , True }

$\endgroup$
1
  • $\begingroup$ Thanks for a thorough answer to my question, accepted! In case you're interested, what I'm trying to do is to make a debug function that allows me to do essentially analyzeConditional[p == q || p == q+1], which would print "p == q is False", "p == q + 1 is True", and so on, decomposing the conditional at boolean operators and printing out each subcondition, and then finally returning the value of the whole conditional. The purpose is to debug a situation where an algorithm terminates at a reasonably complex combination of conditions, and I want to know at which subcondition. $\endgroup$
    – Timo
    Jun 3, 2014 at 5:49
1
$\begingroup$

I propose using the step function I presented in: How do I evaluate only one step of an expression?
(For the remainder of this answer I will assume you have that function loaded.)

I wrote it for the purpose of handling delayed definitions such as:

expr := (p == q || p == q + 1)

You could use it in the following manner:

SetAttributes[f, HoldFirst]
f[expr : (_And | _Or)] :=
  (Print["LHS is ", Unevaluated[expr][[1]]];
   Print["RHS is ", Unevaluated[expr][[2]]])
f[expr_?ValueQ] := f @@ step[expr]

Now:

p = 2;
q = 1;
f[expr]

LHS is False

RHS is True

If the argument has an apparent head of And or Or the first definition is used. If not ValueQ is used to check to see of the expression has a value, and if it does it is partially evaluated with step then passed back to f using Apply. Note: ValueQ can leak side-effects but it is fine for basic purposes such as this.

See Setting options of expressions similar to using SetOptions on objects for other examples.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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