Lecture from: 08.10.2024 | Video: Videos ETHZ

Functions

Look at: Functions first.

Function Overloading: Same Name, Different Meanings

One of the powerful features of functions is overloading – defining multiple functions with the same name but different parameter lists. The compiler figures out which version to call based on the arguments you provide. This lets you create flexible and expressive code.

// Prints a single message
public static void printMessage(String message) { 
    System.out.println(message);
}
 
// Prints a message with a prefix
public static void printMessage(String message, String prefix) { 
    System.out.println(prefix + " " + message);
}

Important: When using function overloading, ensure that the function signatures (name and parameters) are distinct enough to avoid confusion or errors.

Loops

Look at: Loops Repeating Actions with Precision first.

Increment and Decrement Operators

When working with loops, you’ll often need to modify a counter variable to control how many times the loop runs. This is where increment and decrement operators come in handy:

  • Post-increment/Decrement (i++, i--): These operators increment or decrement the value of a variable after its current value is used in the expression.

    • Example: x = i++; first assigns the current value of i to x, then increases i by 1.
  • Pre-increment/Decrement (++i, --i): These operators increment or decrement the value of a variable before its value is used in the expression.

    • Example: x = ++i; first increases i by 1, then assigns the new value of i to x.

While i++ and i-- are the most common, Java offers a whole suite of shorthand operators for modifying variables directly within expressions. These are called “compound assignment operators.”

  • Addition: +=: Equivalent to x = x + y; (Example: count += 1;)
  • Subtraction: -=: Equivalent to x = x - y; (Example: total -= cost;)
  • Multiplication: *=: Equivalent to x = x * y; (Example: area *= width;)
  • Division: /=: Equivalent to x = x / y; (Example: average /= count;)
  • Modulus: %=: Equivalent to x = x % y; (Example: remainder %= 10;)

Side Effects

Let’s examine a scenario that highlights side effects, those unintended consequences that can occur within expressions.

Example:

boolean p = ...;  // The initial value of 'p' is unknown
int x = 2;
boolean q = p && x++ < 3;
System.out.println("x: " + x); // What is x going to be?

Breakdown:

  1. Initialization: We start with x set to 2, but the initial value of the boolean variable p is unspecified.
  2. The Expression (q = p && x++ < 3): This expression’s behavior depends on the value of p:
    • Case 1: p is true:
      • The short-circuiting doesn’t apply because the first condition (p) is already true.
      • The comparison x < 3 evaluates to true (2 is less than 3). Therefore, q becomes true.
      • Crucially, the post-increment operation x++ executes after the comparison. This increases x by 1, making x equal to 3.
    • Case 2: p is false:
      • Short-circuiting comes into play – the entire expression evaluates to false without ever reaching x++. Therefore, q becomes false, and x remains unchanged at 2.

Side effects are actions within expressions that change a program’s state unintentionally. In our example:

  • The intended outcome was likely determining whether q was true or false based on the values of p and x.
  • However, the side effect is the modification of x only when p is true.

The Fencepost Error: When Counting Goes Wrong

Wikipedia: Fencepost Error

Imagine you’re tasked with building a fence along a rectangular field. You need to install posts, one at each corner and every n meters in between. Seems straightforward, right? You might think: “If the field is n meters long, I just need n posts!”

But here’s the catch: you also need a post at each end of the field! This common oversight is known as the fencepost error.

The Loop Analogy:

To avoid the fencepost error, remember these key points:

  • Include Endpoints: Always ensure that both the first and last elements are accounted for.
  • Adjust Loop Range: When dealing with physical quantities, consider starting your loop range from 0 or 1, depending on the situation, to explicitly include both endpoints.

Do-While Loops

do-while loops are particularly useful when you want to guarantee that the code block executes at least once, even if the condition checking initially evaluates to false.

Structure:

do {
    // Code to be executed repeatedly
} while (condition);

Example:

int count = 0;
do {
    System.out.println("Iteration: " + count);
    count++;
} while (count < 5);

This code will print:

Iteration: 0
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4

When to Use do-while:

  • When you need to ensure that a piece of code executes at least once, regardless of the initial condition.
  • For user input validation – where you want to keep prompting for input until a valid value is entered.