How does mafia evaluate if statements?

zarqon

Well-known member
I have a question for our beloved mafia coders, which I am asking publicly because I believe the answer will be useful to all: how does mafia evaluate if statements with multiple conditions?

I searched as best I could and only found this hopeful but slightly mysterious allusion:

[quote author=Veracity link=topic=161.msg798#msg798 date=1147531250]
&& and || don't always evaluate the rhs, of course
[/quote]

Which inclines me to believe mafia first evaluates the left side of the && or || and if that expression evaluates as false, it does not attempt (or need) to evaluate the right side.

Which, happily, would mean situations like this are safe:

Code:
string[string] somemap;

if (somemap contains "somevalue" && somemap["somevalue"] == "Yay!") {
   ...do stuff...
}

It would also mean that we can reduce chains of if statements:

Code:
if (condition1) {
   if (condition2) {
      if (condition3) {
         ...do stuff...
      }
   }
}

to a single statement:

Code:
if (condition1 && condition2 && condition3) {
   ...do stuff...
}

It appears from Veracity's admittedly dated quote that mafia works this way (left-first; if false, don't evaluate right). Is that a correct assumption?

(phrased as a "yes/no" question... I value your time. :) )
 

Veracity

Developer
Staff member
Yes. They are just like && and || in any "C-like" language, and like (and) and (or) in Lisp.
 

zarqon

Well-known member
Hot dang. Code optimizations underway.

Thanks for making such a useful and mature language.
 
[quote author=hippymon link=topic=1488.msg6924#msg6924 date=1201750390]
http://www.wiki.kolmafia.us/index.php?title=If
[/quote]

In writing of that page, I did not go that into depth. They are is referring strict boolean evaluation vs loose boolean evaluation. As of the time of writing The following held true:

strict
Code:
if((false) && (this code get's evaluated also))

even though kolmafia already knew that the answer was false after the first part of the statement, kolmafia evaluated the whole if statement. if a function call was in the second part, then that function got called. We might not want the second part of the if statement (a function call) to be called if the first part holds false. This would require a nested if statement to achieve.

from what I gather from reading this thread the now following holds true

loose
Code:
if((false)&&(This code never get's evaluated))

since kolmafia knows that the whole statement evaluates to false, it does not need to evaluate the second part so it is ignored. This can be a code optimization and make scripts run faster, but in some cases the function called in the second part may need to be called anyway this would require an else statement which has it's own set of downfalls.

As long as the user writing the script knows how it is handled then they should be able to write scripts which fill their needs. Not knowing can raise some confusion if you expect it to act one way, but it does the other.
 

Paragon

Member
I don't know if if ash scripts follow short circut boolean expression evaluation or not, but I noticed an error at the top that hasn't been fixed or pointed out and I just wanted to address it before it caused anyone any problems.
if a && b will always return false if a is false, if a is true you must evaluate b.

on the other hand

if a || b will always return TRUE if a is true, if a is false you must evaluate b.

also something interesting which also comes from this line of thought is
if (a && !a) will ALWAYS return false while if (a || !a) will ALWAYS return true

also the post which state that we can reduce if chains such as
if (a) then
if (b) then
If (c) then
[do stuff]
}
}
}
Can already be shortened to
if (a && b && c) or even if (a && c && b) because if (a && b) && c is the same as if a && (b && c)
However, you always have to remember that if (a && b) || c is NOT the same as if a && (b || c)
at the same time
if (a)
if (b || c)
}
}
IS the same as if (a && (b||c))

The only time that the multiple if chain is required is if something are done even if the expressions nested further within the block are true or false. I.E
if (a)
do something
if (b)
do something
if (c)
do something
}
}
}
That statement must be left the way it is because there are actually four diffrent outcomes that can occur based on the state of a,b and c rather then just the two that would be available if it were shortened to a single statment. This is proably all very basic stuff for the majority of people here, but I just wanted to clear some things up before it potentially caused confusion to anyone.
 
[quote author=Paragon link=topic=1488.msg6979#msg6979 date=1202002246]
The only time that the multiple if chain is required is if something are done even if the expressions nested further within the block are true or false. I.E
if (a)
do something
if (b)
do something
if (c)
do something
}
}
}
That statement must be left the way it is because there are actually four diffrent outcomes that can occur based on the state of a,b and c rather then just the two that would be available if it were shortened to a single statment. This is proably all very basic stuff for the majority of people here, but I just wanted to clear some things up before it potentially caused confusion to anyone.

[/quote]

Consider this:

Code:
if((item_amount($item[bat wing]) >= 1) && eat(1, $item[bat wing]))
  adventure(2, $location[entryway]);

we don't want to try to eat a bat wing if we don't have 1 or kolmafia will buy 1.

Code:
if(item_amount($item[bat wing]) >= 1) 
   if(eat(1, $item[bat wing]))
     adventure(2, $location[entryway]);
would be the same.

Code:
if(((item_amount($item[bat wing]) >= 1) && eat(1, $item[bat wing])) || ((item_amount($item[knob sausage]) >= 1) && eat(1, $item[knob sausage])))
  adventure(2, $location[entryway]);

In both parts of the if statement above we don't want to try to eat the item if we do not have it. We also do not want to try to adventure if we haven't ate anything. If we throw in a few more, the scripts length (line count) can easily start to get out of control.

Code:
if( ((item_amount($item[bat wing]) >= 1) && eat(1, $item[bat wing])) ||
    ((item_amount($item[knob sausage]) >= 1) && eat(1, $item[knob sausage])) ||
    ((item_amount($item[rat appendix]) >= 1) && eat(1, $item[rat appendix])) ||
    ((item_amount($item[lime]) >= 1) && eat(1, $item[lime])) ||
    ((item_amount($item[cherry]) >= 1) && eat(1, $item[cherry])) ||
    ((item_amount($item[orange]) >= 1) && eat(1, $item[orange])) )
        adventure(2, $location[entryway]);

That last snippet is just something to consider.how long could that if statement become? using another format how long would code that does the same become?

Heck, I dunno that I would ever actually use it myself, but it's in some peoples programming style.

[quote author=hippymon link=topic=1488.msg6982#msg6982 date=1202014212]
Actually it would be:

if (a) then
if (b) then
if (c) then
[do stuff]

[/quote]

since we are correcting code here, I will point out that this isn't pascal (or other language using if..then format) so:
if (a)
if (b)
if (c)
[do stuff]
 
Top