if statement short circuiting

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
mc2software
Posts: 22
Joined: Wed Jan 13, 2021 6:08 pm
Location: Northern Florida

if statement short circuiting

Post by mc2software » Thu Mar 25, 2021 7:51 pm

I'm new to Python and MicroPython so a circumstance came up where I was wondering of those who knew more about the Python language and processing might know about the execution of "if" statement conditions and whether there's any short-circuiting with "if" statements that have multiple conditions, for example: if a > b and b > c and c < d: In some languages the execution will immediately move on to the next line of code if the first condition fails, while others will evaluate every condition before it moves onto the next. In the case of the former, putting together an "if" statement with a 1/2 dozen conditions will run faster the first condition fails, and in some cases the latter will also use up more memory.

rafl
Posts: 10
Joined: Tue Mar 23, 2021 7:15 am

Re: if statement short circuiting

Post by rafl » Thu Mar 25, 2021 9:02 pm

You can use expressions with observable side-effects to find out what the implementation is doing:

Code: Select all

if False && foo[0]:
    pass
will succeed, while

Code: Select all

if True && foo[0]:
    pass
will tell you that foo isn't defined.


Out of curiosity, which languages have a different evaluation-order for their boolean operators?

stijn
Posts: 735
Joined: Thu Apr 24, 2014 9:13 am

Re: if statement short circuiting

Post by stijn » Fri Mar 26, 2021 7:25 am

https://docs.python.org/3/library/stdty ... and-or-not so yes, supported.

Also wondering which languages (which are effectively in use) do not support short circuiting.

nekomatic
Posts: 37
Joined: Thu May 08, 2014 9:31 pm

Re: if statement short circuiting

Post by nekomatic » Tue Mar 30, 2021 12:09 pm

Well, MATLAB has short-circuiting and non-short-circuiting versions of the logical operators:

Code: Select all

x = false && undefined
executes without an error, but

Code: Select all

x = false & undefined
fails.

(The non-short-circuiting version can operate on array as well as scalar inputs - and it does short-circuit if it's in the condition of an if or while.)

mc2software
Posts: 22
Joined: Wed Jan 13, 2021 6:08 pm
Location: Northern Florida

Re: if statement short circuiting

Post by mc2software » Tue Mar 30, 2021 3:45 pm

From what I've read since I posted this, Python does short-circuit if statements.

There are a few languages and what I'll call DEs (development environments) that don't short-circuit, such as MSSQL and C# (at least for some boolean expressions). To short-circuit in C# and SQL you need to use "case" statements. I'm not a big VB developer, but since they're all based on the CRL, I suspect that any CRL will not short-circuit.

My query originated largely because there is no "case" handling in Python and I'm trying to optimize if statements that have multiple conditions. In languages where there is no case, I'd do a cascading (b-tree style) if statement to reduce the number of times a condition was tested, but that makes it harder to test and read. Now that I know it short-circuits, I just have to figure out a good way to emulate a case statement.

stijn
Posts: 735
Joined: Thu Apr 24, 2014 9:13 am

Re: if statement short circuiting

Post by stijn » Tue Mar 30, 2021 6:13 pm

Fair point about Matlab. Funny, because I use it from time to time, but never looked at it that way. Nor realized it does short-circuit sometimes and sometimes doesn't.
I just have to figure out a good way to emulate a case statement
Hard to tell without actual code, but if it's really like one big case you could use a dict instead i.e.

Code: Select all

print({'a': 0, 'b': 1, 'c': 2}['b']) # Prints 1

mc2software
Posts: 22
Joined: Wed Jan 13, 2021 6:08 pm
Location: Northern Florida

Re: if statement short circuiting

Post by mc2software » Thu Apr 01, 2021 10:28 pm

I've researched using a dictionary to substitute for a case statement, but that seems to work only if you're just returning a value, say month of the year, or day of the week. I'm having a hard time figuring out how to make that useful for executing a function. For example, here's a case statement from an Arduino project:

Code: Select all

void displaySegment(int controller, int digit, char segment)
{
  // testLED(digit, "segment : " + segment);
  switch (segment)
  {
    case 'A':
      {
        lc.setRow(controller, digit, B01000000); // segment A
        break;
      }
    case 'B':
      {
        lc.setRow(controller, digit, B00100000); // segment B
        break;
      }
    case 'C':
      {
        lc.setRow(controller, digit, B00010000); // segment C
        break;
      }
    case 'D':
      {
        lc.setRow(controller, digit, B00001000); // segment D
        break;
      }
    case 'E':
      {
        lc.setRow(controller, digit, B00000100); // segment E
        break;
      }
    case 'F':
      {
        lc.setRow(controller, digit, B00000010); // segment F
        break;
      }
    case 'G':
      {
        lc.setRow(controller, digit, B00000001); // segment G
        break;
      }
    case 'P':
      {
        lc.setRow(controller, digit, B10000000); // decimal place
        break;
      }
    case 'N':
      {
        lc.setRow(controller, digit, B00000000); // none
        break;
      }
  }
}
Is there a way to use a dictionary for something like that?

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: if statement short circuiting

Post by dhylands » Thu Apr 01, 2021 11:29 pm

You could use a lambda function as the value. Something like this:

Code: Select all

case = {
  'a': lambda: print('case a called'),
  'b': lambda: print('case b called'),
}
case['a']()
case['b']()

case2 = {
  'a': lambda x: print('case a called, x =', x),
  'b': lambda x: print('case b called, x =', x),
}
case2['a'](123)
case2['b']('some string')
will give these results:

Code: Select all

case a called
case b called
case a called, x = 123
case b called, x = some string
For more complex things you can use a function:

Code: Select all

def foo(arg):
  print('foo called with arg =', arg)

case3 = {
  'd':  foo,
  'e':  lambda arg: print('lambda called arg =', arg)
}
case3['d']('argument to foo')
case3['e']('argument to lambda')
and you can return values as well:

Code: Select all

def bar(num):
    return num * 2

case4 = {
  'f': bar,
  'g': lambda arg: arg * 4
}
print("Result of calling case4['f'] =", case4['f'](7))
print("Result of calling case4['g'] =", case4['g'](7))
which produces

Code: Select all

Result of calling case4['f'] = 14
Result of calling case4['g'] = 28

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: if statement short circuiting

Post by pythoncoder » Fri Apr 02, 2021 1:49 pm

The general point here is that in Python a callable is a first class object and can be stored in dictionaries, lists or sets. Though I doubt there's much application for the latter. You can pass callables as arguments to other callables, and return them as results. This enables a lot of flexibility.

A callable is anything that accepts function call syntax, including functions, methods, class constructors and objects with a __call__ special method.
Peter Hinch
Index to my micropython libraries.

mc2software
Posts: 22
Joined: Wed Jan 13, 2021 6:08 pm
Location: Northern Florida

Re: if statement short circuiting

Post by mc2software » Sun Apr 04, 2021 10:20 pm

Thanks for the help.

I've rewritten

Code: Select all

def drawCircle(display, digit, speed=1):
    
    display.setRow(display,digit,0b01000000) #Segment A
    sleep(speed)
    display.setRow(display,digit,0b00100000) #Segment B
    sleep(speed)
    display.setRow(display,digit,0b00010000) #Segment C
    sleep(speed)
    display.setRow(display,digit,0b00001000) #Segment D
    sleep(speed)
    display.setRow(display,digit,0b00000100) #Segment E
    sleep(speed)
    display.setRow(display,digit,0b00000010) #Segment F
    sleep(speed)
    display.setRow(display,digit,0b00000000) #clear last segment
    sleep(speed)
as

Code: Select all

def segmentBinary(segment):
    switcher={
        'A' : 0b01000000,
        'B' : 0b00100000,
        'C' : 0b00010000,
        'D' : 0b00001000,
        'E' : 0b00000100,
        'F' : 0b00000010,
        'G' : 0b00000001,
        'P' : 0b10000000,   #dot
        'N' : 0b00000000,   #no segment
    }
    return switcher.get(segment, 0b00000000)

def drawCircle(display, digit, speed=1):
    segs = ['A', 'B', 'C', 'D', 'E', 'F', 'N']
    for seg in (segs):
        display.setRow(display,digit,segmentBinary(seg))
        sleep(speed)
Easier to read and manage. The only issue is that I think Python is converting the binary values into an integer and it's messing up the call to setRow().

Post Reply