Don't call an important func as part of an OR test

BlitzMax Forums/BlitzMax Programming/Don't call an important func as part of an OR test

Grey Alien(Posted 2007) [#1]
Gah, I just got stuck on a bug for about an hour. Looked everywhere until by fluke I found out what I'd done and then it was obvious how I'd cocked up.

Consider this:

Local Anim=AnimFunction1()
Anim = Anim or AnimFunction2()

So basically I want anim to be set if either anim function returns true because and animation is occuring.

Here's the dumbass problem: due to incomplete boolean evaluation (meaning as soon as the test is satisfied, the rest of the clauses are ignore) if AnimFunction1 returns true, anim=1 and therefore the second line goes "OK anim=1 so I don't even need to look at the part after the OR" and so AnimFunction2 never gets called! This is no good if both functions are supposed to run EVERY frame.

Anyway fixed it with:

Anim = AnimFunction2() or Anim


Simple, unless later on Mark decides to optimise Blitz by saying, hmm the second part of the OR is only a variable test, thus is quicker to check than the function so I'll do that first. That would break my code and lots of others I expect...so it's very unlikely :-) phew.

Doh!


CS_TBL(Posted 2007) [#2]
Ooh that looks like a valid stinker.. :P Yet it's a proper example of the fact that users can come up with more bugs than BRL can anticipate on from scratch. :P


FlameDuck(Posted 2007) [#3]
That would break my code and lots of others I expect...so it's very unlikely :-) phew.
ACtually that's pretty likely. In fact I believe to some extent it already does this (ie. evaluates the expression it thinks is faster, rather than left to right).

I'm more surprised the syntax is actually legal (and failing that, does what you expect).


Nelvin(Posted 2007) [#4]
This is how all programming languages I know work and it's called shortcut evaluation so it's how it really should (and does) work.


Grey Alien(Posted 2007) [#5]
In Delphi that Syntax is totally legal and it does left to right evaluation. It seems to be the same in BMax BUT maybe we need some official confirmation? Oh and I used that style for my last 2 games BMax games and they worked fine.

Nelvin: Which way is called shortcut evaluation? Left to right or actually checking all the conditions to see which can be tested fastest?


ziggy(Posted 2007) [#6]
Why not use byte operators for this kind of calculations. I mean the | operator.

SuperStrict
If True | Test() Then
	Print "Hello"
End If

If True or Test() Then
	Print "Hello"
End If

Function Test:Int()
	Print "Called!"
	Return True
End Function

This is the output I get:
Called!

Hello

Hello


I thing this is one of the strong points of BlitzMax. you can use logical operations in a optimized way, or arithmetic boolean operations.


Grey Alien(Posted 2007) [#7]
I dunno? (OK Ziggy, just seen your test example, nice tip thanks)

here's another related thread:

http://blitzbasic.co.nz/Community/posts.php?topic=58687

Oh and here's the Delphi proof:

http://www.delphibasics.co.uk/RTL.asp?Name=$B


grable(Posted 2007) [#8]
Its called "short circuit" and as said before is pretty normal.
And really the preferred way too, why waste unnecessary cycles when the test has already been satisfied?


Grey Alien(Posted 2007) [#9]
Yeah I've just been researching it some more, I realise it's normal as I've been using it for years. Flameduck threw doubt on if it was left to right but it really does seem that it's that way. So all is safe ... for now ...


Brucey(Posted 2007) [#10]
It makes sense that if the first part of the statement is true, there's absolutely no point going any further.

Does the second part of the Or make it *even truer* if the first part is true?

:-p


ziggy(Posted 2007) [#11]
The point is using logical operators for arithmetic operations. that's the problem. (see example above). If you want to be sure every part of the expression is fully evaluated, use the arithmetic boolean operations ( | for Or, & for And, and ~ for Xor)


JoshK(Posted 2007) [#12]
Your code is pretty wacky.

Why not this?:

if ( AnimFunction1()+AnimFunction2() ) then...


Grey Alien(Posted 2007) [#13]
Your code is pretty wacky.
yeah it jsut eneded up that way. I'll just do Anim = AnimFunction1()+AnimFunction2()


SculptureOfSoul(Posted 2007) [#14]
Yeah, short circuiting is an intentional design to save unnecessary evalutation. AND works that way too, though in reverse of course (first return of false kills evaluation).

Simple, unless later on Mark decides to optimise Blitz by saying, hmm the second part of the OR is only a variable test, thus is quicker to check than the function so I'll do that first. That would break my code and lots of others I expect...so it's very unlikely :-) phew.



Man, that's one thing that is so great about Lisp. You could, if you were looking for ultimate efficiency, easily write your own macro to see what kind of arguments you are dealing with and then generate the appropriate code - look up variables before function calls. It'd all be handled at compile time based on the arguments you are calling it with, so you'd suffer no runtime performance.

Anyways, your code will work as is but wouldn't it be possible for the animation function itself to increment an animation counter when the animation starts and then decrement it when the animation stops? This way you wouldn't have to call look-up functions to see if an animation is running - you'd just check the "AnimInProgress" variable.

Too bad inlining isn't an option or I'd say just inline your lookup calls and you'd have the best of both worlds - good efficiency and code clarity.


Grey Alien(Posted 2007) [#15]
you'd just check the "AnimInProgress" variable.
All the anims have different counters and so I use an anim var to track the output of all certain anim functions to decide if I should be block player input or not.


WendellM(Posted 2007) [#16]
if AnimFunction1 returns true, anim=1 and therefore the second line goes "OK anim=1 so I don't even need to look at the part after the OR" and so AnimFunction2 never gets called!

Yes. If I were writing a program like that, my instinct would be to:

Local Anim1 = AnimFunction1()
Local Anim2 = AnimFunction2()
If Anim1 Or Anim2 Then

But then I'm old-fashioned... My primate brain never tries for excessive cleverosity with my dull, excessively easy-to-understand code (hey, it learned programming in the early 80s and never much grew out of that). <g>


Chroma(Posted 2007) [#17]
Yeah I just spent a couple hours hunting down a bug that was caused by calling MidHandleImage on an image that was odd numbered in height. It was trying to draw it at 2 places and was blurred.

GA, meant to ask you about the mouse lag thing. I put in a Delay 1 right before Flip 0 and my mouse lag is non-existant. Works well with delta time that I use. Did you end up having to fiddle with the module source?

If Mode = 0 Then Delay 1     'Only delay 1 for windowed mode
Flip 0



Grey Alien(Posted 2007) [#18]
WendellM: Yeah that's what I changed the code to when testing and realised my mistake

Chroma: tip. Make sure ALL images are even sized AND have a one pixel empty border arounf them.

As for mouse lag, originally I had to fiddle with the module source but not with a Delay, with a DX call. The mouse lag is in DX only right? Anyway in the end BRL incorporated the fix into their module so you shouldn't be getting a lag if you are up to date now. Well maybe a one frame lag but not a major one like before.


SculptureOfSoul(Posted 2007) [#19]
All the anims have different counters and so I use an anim var to track the output of all certain anim functions to decide if I should be block player input or not.


Well, I'm not sure how your code works as I'm not fully understanding in your original example why both Anim1() and Anim2() need to be called if either returns true. If either returns true, you know you can block the input right? So why does the second function need to be called anyways?

It seems like you're making a single function handle two tasks - keeping track of the state of the animation and perhaps advancing it forward, as well as reporting the state of that animation. Wouldn't it be better to separate those two tasks...so you'd have something like

UpdateAnimations()

that gets called every frame regardless, and then

IfAnimationsRunning()

which checks the status of the animations?


Grey Alien(Posted 2007) [#20]
possibly. It's no big deal though. Those Anim functions actually have different names in my code and handle different animation aspects of the game. I could wrap them in another function which returns the status of if an anim is occuring but there is no need in my case. There are some other functions also that I call and the end result is that if any of the functions return 1, then I need to block user input. It works well and is understandable. Thanks for the advice.