Now what would you expect this to print out?

Jonathan McKeown jonathan at hst.org.za
Wed May 21 07:46:53 UTC 2008


On Tuesday 20 May 2008 16:44, RW wrote:
> On Tue, 20 May 2008 11:33:50 +0200
>
> Jonathan McKeown <jonathan at hst.org.za> wrote:
> > On Tuesday 20 May 2008 02:41, RW wrote:
> > > On Mon, 19 May 2008 21:46:03 +1200
> > >
> > > Jonathan Chen <jonc at chen.org.nz> wrote:
> > > >     find /usr/src \( -name Makefile -or -name '*.mk' \) -print
> > >
> > > Why does that make a difference, when print always evaluates to
> > > true?
> > >
> > > x AND true   =   x
> > >
> > > so
> > >
> > > (a OR b) AND true   =   a OR b
> > >  a OR (b AND true)  =   a OR b
> >
> > It makes a difference (as in programming) because -print is used for
> > its side-effect rather than its value, and the binding order
> > influences when the side-effect happens.
>
> That's still a bit counter-intuitive because in normal programming
> languages the binding order modifies side-effects via the evaluation
> order. And in both cases the evaluation order would be expected to be
> left-to-right, with -print running last.

Yes. I'm actually talking rubbish. find evaluates its argument expression 
left-to-right, and the ``precedence'' actually applies to term grouping 
rather than evaluation order. (This does affect the outcome, but not in the 
way I glibly said it did).

What I should have said is that like a lot of programming languages, find is 
lazy when it comes to Boolean expressions: when it gets a TRUE in an -or or a 
FALSE in an -and, the value of the whole expression must be TRUE or FALSE 
respectively, regardless of what the remaining terms are, so why bother 
evaluating them? (It's usually referred to as short-circuiting).

> I guess what you are saying is that the side-effect of print is based-on
> a Boolean "running-value". And without the brackets, the first test  has
> been evaluated, but not yet ORed into that "running-value", by the time
> that print runs.

That's not quite how it works. Rewriting

find /usr/src -name Makefile -or -name '*.mk' -print

using extra parens to emphasise the implicit grouping, and including the 
implicit -and, gives:

find /usr/src -name Makefile -or \( -name '*.mk' -and -print \)

in other words, an -or with two terms, one of which happens to be an 
expression.

If -name Makefile is true, the -or is satisfied, so nothing else is evaluated, 
and find goes on to the next filename.

Otherwise, the expression in the second term has to be evaluated. If -name 
'*.mk' is false, the -and is satisfied (which also satisfies the -or) and 
find moves to the next filename. If it's true, the -and can't be satisfied 
without evaluating the -print. The end result is that only files matching 
'*.mk' are printed.

Rewriting the other case,

find /usr/src \( -name Makefile -or -name '*.mk' \) -and -print

If the first expression is false, the -and is satisfied and the -print is not 
evaluated. If the first expression is true (meaning either of the -name 
arguments is true), then the -and can't be satisfied without evaluating the 
-print.

The last case is

find /usr/src -name Makefile -or -name '*.mk'

find quickly analyses this, finds no output action, and converts it to the 
second form above, internally placing parens around the whole expression and 
an -and -print after it.

Jonathan


More information about the freebsd-questions mailing list