Lecture Notes: Decision Making, Control Structures, and Boolean Logic in Programming
Overview of programming flow
- Sequential programming: starts at the beginning, executes every line once and only once until the end.
- Decision making: introducing control flow that may skip or execute a line based on a condition (an if statement).
- Future topics preview: while loops (repeat), for loops (iterate), then data structures and code reuse via libraries.
Data structures and data types (foundational ideas)
- Data structures help manage multiple items (e.g., a class of 20 students) with a uniform processing approach.
- Data types discussed:
- Integer: whole numbers, e.g., 1, 2, 3
- Float: decimal numbers, e.g., 1.0, 2.897
- Long/Double variations exist across languages (examples given:
- 2.69378036 as a long/float representation)
- String: sequence of characters in quotes, e.g., "hello world"
- Char: a single character, e.g., 'a' or '6'
- Boolean: true or false only
Boolean logic and relational operators
- Booleans are used to drive decisions in if statements.
- All numeric comparisons rely on relational operators:
- Less than: {<}
- Greater than: {>}
- Less than or equal to: {\leq}
- Greater than or equal to: {\geq}
- Equal to: {=} (note: in many languages this is == for comparison; single '=' is assignment in many languages)
- Not equal to: {\neq}
- In Visual Logic, the single equal sign is used for either assignment or equality depending on context; many languages require a double equal for comparison.
- Truth values: a relational expression evaluates to true or false.
Assignments vs comparisons (a common confusion)
- Assignment: puts the value on the right-hand side into the left-hand side variable using '=' in many languages.
- Comparison: tests whether two values are equal; often written as '==' in languages like C/Java/Python.
- Visual Logic example uses '=' for both in some contexts; be mindful of your language syntax.
The structure of a relational expression (conditions)
- A condition is a Boolean expression that typically uses relational operators to compare variables or literals.
- Example intuition: a < b, a
- Example interpretation: If x equals 2 and y equals 3, the expression x < y would be true.
- Importance: conditions are the gateways for taking true vs false branches in flow control.
Basic if statements and control flow
- If statements select between actions:
- The true branch executes when the condition is true.
- The false branch (else) executes when the condition is false.
- Trailing else: used for a default value or error-checking path.
- You may nest if statements within true or false branches to handle more complex logic.
- Any legal statement in the language can appear inside an if/else branch (including further ifs or loops).
Nested if statements (embedded ifs)
- Nested/embedded if means an if statement inside the true or false branch of another if.
- Example rationale: if two numbers are equal, you don’t need to check whether they are less than or greater than – that would be redundant.
- When nesting, the outer condition gates the inner checks; if outer is false, inner checks aren’t evaluated.
- Practical intuition: only three numeric states exist for any numeric comparison: equal, less than, greater than.
All-in-one example: payroll calculation with overtime (paycheck example)
- Given: hours worked and hourly rate; regular hours assumed to be 40.
- Pay calculation (conceptual):
- If hours <= 40: pay = hours * rate
- If hours > 40: pay = 40 * rate + (hours - 40) * rate * 1.5
- Improved practice: use a constant for regular hours and for the overtime multiplier to avoid hard-coding numbers.
- Example constants:
- Regular hours constant: \text{REGULAR_HOURS} = 40
- Overtime multiplier: \text{OVERTIMEeta} = 1.5
- Improved formula using constants:
- If hours <= \text{REGULAR_HOURS}: pay = hours * rate
- Else: pay = \text{REGULAR_HOURS} * rate + (hours - \text{REGULAR_HOURS}) * rate * \text{OVERTIMEeta}
- Practice tips: avoid embedding numbers; prompt for inputs clearly (e.g., “Enter hours” and “Enter rate”); consider naming conventions (uppercase with underscores) to indicate constants.
- Example test cases for verification (easy numbers):
- Hours = 50, Rate = 10 → pay should reflect 40 hours at 10 and 10 hours overtime at 15.
- Hours = 40, Rate = 10 → pay should be 400 (no overtime).
- Hours = 30, Rate = 10 → pay should be 300.
Practical design decisions and coding style
- Use constants to improve readability and maintainability.
- Separate input handling from calculation logic for clarity.
- Pseudocode often uses words like then and end if; pseudocode variations exist, but the core logic remains the same.
- Before coding, write a pseudocode/algorithm outline to ensure correct logic flow; then add line comments and finally implement in the target language.
- It’s okay to consult outside resources for ideas, but you must cite sources and produce original work with your own explanations.
Pseudocode vs actual code (notation and practice)
- Pseudocode commonly uses: then, end if, end for, end while.
- Pseudocode is not meant to be executed; it’s a high-level description of the algorithm.
- You might encounter variations in pseudocode syntax across textbooks or courses; adapt as instructed by your teacher.
Control structures: compound vs nested vs sequential conditions
- Compound condition: combines two or more Boolean expressions with a logical operator and is evaluated as a single condition.
- Nested condition: one condition is inside another’s true or false branch (surgical narrowing of decisions).
- Sequential (independent) conditions: separate if statements that do not depend on each other (e.g., age check and state residency check, each producing its own message).
- When to use which:
- If one condition depends on another, use nested/compound appropriately.
- If conditions are independent, sequential ifs can be clearer and avoid unnecessary coupling.
Logical operators (and, or, not, exclusive or) and short-circuiting
- Logical AND (and): both expressions must be true for the whole to be true.
- Logical OR (or): at least one expression must be true for the whole to be true.
- NOT (not): negates a Boolean expression.
- Exclusive OR (exclusive or, XOR): true when exactly one of the operands is true.
- Short-circuit evaluation: in an AND, if the first condition is false, the second is not evaluated; in an OR, if the first condition is true, the second is not evaluated.
- Parentheses: always use parentheses around each condition in a compound expression to avoid ambiguity and ensure correct evaluation order.
- Example truth tables (conceptual):
- And: true only if both conditions are true.
- Or: true if at least one condition is true.
- Not: flips the truth value.
- XOR: true if exactly one condition is true.
- Practical note: in some contexts, you may choose XOR for clarity or efficiency; some courses even emphasize it for hardware design (gates) but many software problems use and/or/not instead.
Example: compound condition for billing window (nested vs compound)
- Nested approach: if time > 6 and time < 18, then charge; else do not.
- Compound approach: if (time > 6) and (time < 18) then charge; else do not.
- Real-world lesson: complex conditions may be written as nested checks or as a single combined condition; choose the form that is clearer and less error-prone for your situation.
- In the billing example, peak hours (greater than 6 and before 18) are charged at a rate (e.g., 0.10 per minute);
- If not within the window, no charge.
Operator precedence and parentheses reminder (programming practice)
- Always group each condition with parentheses when using logical operators to avoid mis-evaluation and to aid readability.
- In Python, indentation and blocks replace curly braces; in many other languages, braces define blocks; different languages have different syntax, but the logical principles remain the same.
Common mistakes and debugging guidance
- Don’t rely on a single example to validate a program; test all meaningful paths (often six test cases for middle-value problems with three inputs).
- When grading or reviewing, expect comprehensive testing data and demonstrations of different input scenarios.
- Do not copy-paste code from online sources without understanding and without providing your own explanation and comments.
- Keep comments clear and explain the reasoning behind each step; this helps instructors verify understanding.
Testing middle-value problems with three numbers (unduplicated inputs)
- Problem: given three numbers a, b, c, determine which is the middle value.
- Six conditions to consider (for unduplicated numbers only):
- If b < a < c or c < a < b, then a is the middle value.
- If a < b < c or c < b < a, then b is the middle value.
- If a < c < b or b < c < a, then c is the middle value.
- If you allow duplicates, you must test additional combinations such as 333, 355, 553, 535, etc. (the six core cases expand when duplicates are allowed).
- Approach options:
- Nested/compound approach: use a primary comparison (e.g., determine the smallest, then the middle, then the largest) and nest subsequent checks accordingly.
- Sequential approach: treat independent comparisons separately where appropriate.
- The goal is to cover all possible input scenarios, including edge cases like duplicates, to ensure robustness.
Real-world takeaways and philosophy
- Visual logic is highly visual for understanding flow: true vs false branches are explicit in flowcharts.
- In larger programs, avoid unnecessary checks to optimize performance (short-circuiting and minimal calculations).
- When communicating with teammates, use consistent naming conventions and emphasize readability (constants in caps, descriptive variable names).
- The lecture emphasizes balancing clarity, correctness, and efficiency in program design, along with responsible debugging and documentation practices.
Quick reminders from the instructor
- You can discuss the concepts of flowcharts, pseudocode, and control structures in class assignments and exams.
- You may be allowed to use notes and lectures but not AI or external help during exams.
- The instructor stresses the importance of testing thoroughly, using the right structure for your language, and maintaining clean, maintainable code.