Day 3: Advanced Conditionals

back · home · slides · CMSC 201 (Fall 2024) @ UMBC · COMPUTER SCIENCE YEAHHH!!!

CMSC 201: Conditionals P2

Agenda:

  • Any more HW1 questions?
  • "Fun": some programming "laws"
  • Who cares? (AKA: what will we make)
  • Advanced conditionals
  • Lists/loops preview
  • Goal: Understand booleans and conditions VERY well; be able to write an if statement for anything! Maybe have some fun :)

    Also my email is: sdonahue@umbc.edu (Shane Donahue)

    HW1

    Still exists. Still due 2024-09-13 23:59.99999.... Any more questions?

    HW1

    "Fun": Compsci "laws"

    People in computer science love making up or referencing "laws" (and effects, and rules, and such). Here are some of my favorites:

  • Goodhart's law: "When a measure becomes a target, it ceases to be a good measure."
  • Parkinson's law: "Work expands so as to fill the time available for its completion." (Stock-Sanford corollary: "If you wait until the last minute, it only takes a minute to do.")
  • Zawinski's law: "Every program attempts to expand until it can read mail. Those programs which cannot so expand are replaced by ones which can."
  • Who cares (about advanced conditionals)?

    Advanced conditionals (and logic gates) make up literally all of computing! And leads to one of the hardest problems in computer science. But this example code is going to be a bit more boring... Let's do some programming interview practice problems! (FizzBuzz)

    Advanced Conditionals

    Review: Conditionals take an action based on a condition. Think: IF this, then THAT.

    if temperature > 70:
        print("summer clothes!")
    if temperature > 70:
        print("summer clothes!")
    else:
        print("maybe wear a jacket")
    if temperature > 70:
        print("summer clothes!")
    elif hvac_working:
        print("who cares, technology saves us from the environment")
    else:
        print("maybe wear a jacket")

    The None type

    Python has a special "type" or object: None. It's the closest thing Python has to a NULL or empty value. It represents a value of nothingness. It is always considered false (False).

    Truthy/Falsy values

    Python tries to be helpful by having things that are not booleans still evaluate to true or false.

    Truthy/falsy values only happen in a "boolean context". (What does that mean? Whenever it's used by itself in some conditonal.)

    # Not truthy/falsy
    if 5 == int(input()):
    	print("Nice number!")
    # Truthy/falsy!
    if int(input()):
    	print("Nice number!")

    Negation

    Want the opposite of something? Use not!

    not True == False
    if not 100 >= 1000:
        print("100 is not >= 1000!")

    Combining conditions

    Python keywords and and or let us combine multiple conditionals into one statement.

    Truth tables

    AND True False
    True TRUE FALSE
    False FALSE FALSE
    OR True False
    True TRUE TRUE
    False TRUE FALSE
    if temperature > 70 and ac_broken == True:
        print("summer clothes!")

    We can group these with parentheses. Normally they are evaluated left to right.

    True and False
    True and (False or True)

    De Morgan's law

    When you negate something, there is an equivalence rule you can use (known as De Morgan's laws). Sometimes this is useful for simplifying expressions. It is:

    not (A or B) --(is equivalent to)--> (not A) and (not B)
    not (A and B) --(is equivalent to)--> (not A) or (not B)
    

    Some intuition: A AND B will only be if both A or B and true. To negate this (NOT AND), we need to have either A or B be true-- but not both! (Think: we're inverting the table.)

    AND True False
    True TRUE FALSE
    False FALSE FALSE
    NOT AND (NAND) True False
    True FALSE TRUE
    False TRUE TRUE

    Let's simplify this:

    if not (user_input > 10 and user_input % 2 == 0) \
       and user_input != 12:
    	print("I like that number!")
    if user_input <= 10 or user_input % 2 != 0 \
       and user_input != 12:
    	print("I like that number!")

    Short circuiting

    When the outcome of a condition with multiple parts is obvious, Python won't bother evaulating unnecessary parts.

    Why are these two code blocks different?

    temperature = 60
    if temperature > 70 and ac_broken == True:
        print("summer clothes!")
    temperature = 80
    if temperature > 70 and ac_broken == True:
        print("summer clothes!")

    Combining our Type lore knowledge: What would this print?

    if print("condition one") and print("condition two"):
        print("wow!")

    What about this one:

    if (print("condition one") or not print("condition two")) \
    	 and print("condition three") \
    	 and not print("condition four"):
        print("wow!")

    Speaking of circuits...

    AND, OR, and NOT gates make up all of computation. Using just those three logic gates gates, you can do anything with a computer. CMPE people are destined to learn all about that in gratuitous detail... but the rest of us can too! (Recommended resources if you're interested: nand2tetris and Code by Charles Petzold).

    Theoretical CS time! (now former Python experience people can't say nothing in CMSC201 was new...)

    Boolean satisfiability problem...

    This example is pretty obnoxious:

    if (print("condition one") or not print("condition two")) \
    	 and print("condition three") \
    	 and not print("condition four"):
        print("wow!")

    What if you we replaced all the conditions with variables?

    if (A or not B) \
        and C \
        and not D:
        print("wow!")

    What True or False values do these variables need to have in order to execute the if body? Simple sounding question, right?

    Is NP-Complete 😱😱😱😱

    It gets difficult quick:

    if (A or not B) \
        and C \
        and Z or not Y \
        or E or W and Z \
        or (Z and A and C or X):
        print("wow!")

    The boolean satisfiability problem is the first problem proven to be NP-complete (non-polynomial) (Cook-Levin). Put another way: there is no efficient solution.

    If you can find an efficient solution to this problem (in "polynomial", or efficient, time), you solve basically every hard computer science problem. This is an active area of research (SAT solvers)! And it's very cool.

    SAT solvers can solve SAT problems! Very slowly. This is code for z3, the most popular SAT solver. Here is our first example before:

    (declare-const A Bool)
    (declare-const B Bool)
    (declare-const C Bool)
    (declare-const D Bool)
    (assert (and (and (or A (not B)) C) (not D)))
    (check-sat)
    (get-model)
    sat
    (
      (define-fun A () Bool
        false)
      (define-fun D () Bool
        false)
      (define-fun B () Bool
        false)
      (define-fun C () Bool
        true)
    )

    Brain expansion: let's prove DeMorgan's law with z3!

    (declare-const a Bool)
    (declare-const b Bool)
    (define-fun demorgan () Bool
        (= (and a b) (not (or (not a) (not b)))))
    (assert (not demorgan))
    (check-sat)
    

    Outputs, "proving" by lack of counter-example:

    unsat
    

    Takeaway: simple problems can very complex very quickly. P problems are "easy" and NP are "hard". Computer science is cool?

    Some code and preview of next time: Loops and lists!

    Loops let you do something as many times as you want! And lists let you store lists of things.

    Trial by FizzBuzz... Legendary interview screening question.

    [...] Replace any number divisible by three with the word "fizz", and any number divisible by five with the word "buzz", and any number divisible by both three and five with the word "fizzbuzz". (via Wikipedia).

    My solution:

    for x in range(1, 101):
        if x % 3 == 0:
            print("Fizz", end="")
        if x % 5 == 0:
            print("Buzz", end="")
        if not (x % 3 == 0) and not (x % 5 == 0):
            print(x, end="")
        print()

    Can we do it with elif?

    Can we do it with a list?