teaching machines

CS 145 Lecture 16 – Logic Cont’d Cont’d

October 17, 2016 by . Filed under cs145, fall 2016, lectures.

Dear students,

In the late 1800s, logician John Venn invented a diagram for showing ideas of logic. He writes:

I began at once somewhat more steady work on the subjects and books which I should have to lecture on. I now first hit upon the diagrammatical device of representing propositions by inclusive and exclusive circles. Of course the device was not new then, but it was so obviously representative of the way in which any one, who approached the subject from the mathematical side, would attempt to visualise propositions, that it was forced upon me almost at once.

Today we’ll have a look at the logical operators through the lens of Venn diagrams. Our two predicates will be isDeciduous and isConiferous. We will examine a variety of subsets of the these two populations and craft logical expressions to represent them.

Next up we’ll examine the following piece of code. What is the output from each statement?

public static void main(String[] args) {
  boolean b;

  // Ands
  b = getTrue() && getTrue();  // pretend we output a line break after each of these
  b = getTrue() && getFalse();
  b = getFalse() && getTrue();
  b = getFalse() && getFalse();

  // Ors
  b = getTrue() || getTrue();
  b = getTrue() || getFalse();
  b = getFalse() || getTrue();
  b = getFalse() || getFalse();
}

public static boolean getTrue() {
  System.out.print("T");
  return true;
}

public static boolean getFalse() {
  System.out.print("F");
  return false;
}

It’s my hope that you printed two letters for each statement. However, when we actually run this, we observe that some statements only print one letter. What gives? Well, we know that && requires both its operands to be true. If the first is false, why bother even asking about the second? This is called short-circuiting because we give up early. Short-circuiting is present in most mainstream languages. With ||, if the the first operand is true, who cares about the second?

This idea is not unique to programming. It is fundamental human logic. Suppose you’re crossing a street and there’s traffic in the nearest lane. You needn’t bother even checking the far lane—especially if it’s empty, as you’ll only get frustrated. Suppose you want to bake cookies, but have no chocolate chips. Why bother checking the cupboards for flour and baking powder? These examples are both short-circuited ANDs. Consider this OR: suppose you accept cash or credit. If somebody gives you cash, you don’t also ask for a credit card.

You might think of short-circuiting as just a technical curiosity, but it probably is more important than that. For one, I saw this job ad just this morning. For two, if often comes in hand to guard against a possible unsafe operation. We might right the dangerous operation as the right operand and the safeguard as the left:

safeguard && unsafe

If the safeguard condition isn’t met, we don’t do the unsafe thing. Consider this unsafe method:

public static boolean hasSameFirstTwoLetters(String s) {
  return s.charAt(0) == s.charAt(1);
}

This is unsafe because s might not have two letters. We can safeguard it:

public static boolean hasSameFirstTwoLetters(String s) {
  return s.length() >= 2 && s.charAt(0) == s.charAt(1);
}

Our final task for the day is a little interactive exercise in collision detection.

This rounds out our formal focus on the various boolean operators. Next time, we start talking about conditionals and loops, which use these booleans to decide what to execute next.

Here’s your TODO list to complete before we meet again:

See you next class!

Sincerely,

P.S. Here’s the code we wrote together in class…

TrueFalse.java

package lecture1017;

public class TrueFalse {
  public static void main(String[] args) {
    boolean b;
    
    b = getTrue() && getTrue(); // TT
    System.out.println();
    b = getTrue() && getFalse(); // TF
    System.out.println();
    b = getFalse() && getTrue(); // FT
    System.out.println();
    b = getFalse() && getFalse(); // FF
    System.out.println();
    
  }

  public static boolean getTrue() {
    System.out.print("T");
    return true;
  }
  
  public static boolean getFalse() {
    System.out.print("F");
    return false;
  }
}

RectangleUtilities.java

package lecture1017;

public class RectangleUtilities {
  public static boolean isColliding(double leftA,
                                    double rightA,
                                    double bottomA,
                                    double topA,
                                    double leftB,
                                    double rightB,
                                    double bottomB,
                                    double topB) {
    return isCollidingHorizontally(leftA, rightA, leftB, rightB) &&
           isCollidingVertically(bottomA, topA, bottomB, topB);

  }
  
  public static boolean isCollidingHorizontally(double leftA, double rightA, double leftB, double rightB) {
    return !(leftA > rightB || rightA < leftB);
  }
  
  public static boolean isCollidingVertically(double bottomA, double topA, double bottomB, double topB) {
    return !(bottomA > topB || topA < bottomB);
  }
}

RectangleMover.java

package lecture1017;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class RectangleMover extends JFrame {
  private Rectangle r1 = new Rectangle(50, 100, 100, 150);
  private Rectangle r2 = new Rectangle(300, 400, 200, 50);
  private Rectangle target;
  private Point downAt;

  public static void main(String[] args) {
    new RectangleMover();
  }

  public RectangleMover() {
    super("Rectangle Mover");
    add(new Panel());
    setSize(1000, 600);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setVisible(true);
  }

  class Panel extends JPanel implements MouseListener, MouseMotionListener {
    public Panel() {
      addMouseListener(this);
      addMouseMotionListener(this);
    }

    @Override
    public void paintComponent(Graphics g) {
      super.paintComponent(g);

      if (RectangleUtilities.isColliding(r1.getMinX(), r1.getMaxX(), r1.getMinY(), r1.getMaxY(), r2.getMinX(), r2.getMaxX(), r2.getMinY(), r2.getMaxY())) {
        g.setColor(Color.RED);
      } else {
        g.setColor(Color.GREEN);
      }

      g.fillRect(r1.x, r1.y, r1.width, r1.height);
      g.fillRect(r2.x, r2.y, r2.width, r2.height);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
      downAt = e.getPoint();
      if (r1.contains(e.getPoint())) {
        target = r1;
      } else if (r2.contains(e.getPoint())) {
        target = r2;
      } else {
        target = null;
      }
    }

    @Override
    public void mouseReleased(MouseEvent e) {
      target = null;
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    @Override
    public void mouseDragged(MouseEvent e) {
      if (target != null) {
        target.translate(e.getPoint().x - downAt.x, e.getPoint().y - downAt.y);
        downAt = e.getPoint();
        repaint();
      }
    }

    @Override
    public void mouseMoved(MouseEvent e) {
    }
  }
}