2
$\begingroup$

Small questions about evaluation process of And, Or.

code 1)

If[4<= f[5] <=9, Print["Good"], Print["Bad"]]

guess 1 for code 1)

  1. Evaluate f[5]
  2. Check 4<=f[5]. Suppose that it was indeed true.
  3. CPU still remember the value of f[5]. Check f[5]<=9 without evaluating f[5] again.

code 2)

If[f[5]<4 || f[5]>9, Print["Bad"], Print["Good"]]

guess 2 for code 2)

  1. Evaluate f[5]
  2. Check f[5]<4. Suppose that it was indeed false.
  3. CPU does not remember the value of f[5]. So evaluate f[5] again. Then, Check f[5]>9.

I would appreciate it if you tell me about the codes and guesses.

$\endgroup$
5
  • 2
    $\begingroup$ Why not Trace? $\endgroup$
    – xzczd
    Commented Aug 26, 2022 at 13:36
  • 2
    $\begingroup$ The reason in part 3. is not so much about remembering f[5], but that there is no guarantee that f[5] evaluates to the same value every time. For instance, RandomReal[5]. $\endgroup$
    – Michael E2
    Commented Aug 26, 2022 at 13:56
  • $\begingroup$ I found my answer with help of Trace. And I couldn't think of Random case. Thank you. $\endgroup$
    – imida k
    Commented Aug 26, 2022 at 14:09
  • $\begingroup$ Look in the help at: Notation/tutorial/PrecedenceOfOperatorsInNotations $\endgroup$ Commented Aug 26, 2022 at 14:36
  • $\begingroup$ The FullForm[4 <= x <= 9] is LessEqual[4, x, 9], which means f[5] should only evaluate once. $\endgroup$
    – Greg Hurst
    Commented Aug 27, 2022 at 14:01

1 Answer 1

5
$\begingroup$

I checked it with Trace and Timing (with the factorial of 10000000) and my conclusion is

For term a<f[x]<b, CPU evaluates f[x] only once.

For term a<f[x]&&f[x]<b, CPU evaluates f[x] twice if a<f[x] is True.

For term f[x]<a||f[x]>b, CPU evaluates f[x] twice if f[x]<a is False.

In fact, if term1 and term2 are terms containing f[x], then, for term1&&term2, if term1 is True, CPU evaluates f[x] again to check term2. Similar logic applies to term1||term2

So, my recommendation is

  1. use the term a<f[x]<b if possible. It is faster and always good.
  2. do not use f[x]<a||f[x]>b, instead use Module[{m=f[x]},m<a||m>b}. it can save time to its half, in case f[x]<a was indeed False.
$\endgroup$
3
  • 1
    $\begingroup$ I would argue that this has nothing to do with computing boolean logic. An expression like f[5] will be evaluated everytime it's encountered during the evaluation process. I wouldn't be surprised if some built-in functions do some optimizations for special forms, but in general, I would never expect the evaluator to remember/cache values for expressions. When building your own f, you can implement memoization, but even that doesn't prevent evaluation of f[5]--it just causes subsequent evaluations to use a different (presumably more efficient) rule. $\endgroup$
    – lericr
    Commented Aug 26, 2022 at 21:46
  • 1
    $\begingroup$ When you're building up an expression and you notice you have, for example, f[5] repeated within the expression, you can do your own optimization with With. I suppose using Module works too, but that form will set up unnecessary variables, whereas With will just slap the values into your expression in situ. This is uesful not only for optimization, but to avoid having to change multiple identical subexpressions when you're refactoring or experimenting. $\endgroup$
    – lericr
    Commented Aug 26, 2022 at 21:50
  • 1
    $\begingroup$ @lericr This is indeed a little related to boolean logic, because evaluation may stop halfway in && and ||. As shown by OP, for expression like expr1&&expr2, the evaluation ends immediately once expr1 is found to be False, and expr2 will never be touched in this case. $\endgroup$
    – xzczd
    Commented Aug 27, 2022 at 3:50

Your Answer

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

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