2  Syntax and Control Flow

Control flow statements determine the order in which code is executed, allowing programs to make logical decisions, perform repetitive tasks, and handle errors efficiently. To learn more, consider watching the following video:

There are three main types of control flow statements:

2.1 Conditional Statements

Conditional statements let a program execute different code depending on whether a condition is true or false.

  • if statement → Executes code only if a condition is true.
  • if-else statement → Executes one block if true, another if false.
  • if-elif-else statement → Checks multiple conditions.

2.1.1 Simple Example

Check if a number is positive, negative, or zero.

Python Code

x = 10

if x > 0:
    print("Positive number")
elif x == 0:
    print("Zero")
else:
    print("Negative number")
Positive number

R Code

x <- 10

if (x > 0) {
  print("Positive number")
} else if (x == 0) {
  print("Zero")
} else {
  print("Negative number")
}
[1] "Positive number"

2.1.2 Medium Example

Check if a number is even or odd, with an additional condition.

Python Code

try:
    x = int(input("Enter a number: "))  

    if x % 2 == 0:
        print(f"{x} is even.")
        if x % 4 == 0:
            print(f"{x} is also a multiple of 4.")
    else:
        print(f"{x} is odd.")

except ValueError:  
    print("Invalid input! Please enter a valid integer.")

R Code

x <- as.integer(readline("Enter a number: "))

if (x %% 2 == 0) {
  print(paste(x, "is even."))
  if (x %% 4 == 0) {
    print(paste(x, "is also a multiple of 4."))
  }
} else {
  print(paste(x, "is odd."))
}

2.1.3 Complex Example

Categorizing a person’s age group.

Python Code

age = int(input("Enter your age: "))

if age < 0:
    print("Invalid age")
elif age <= 12:
    print("You are a child.")
elif age <= 19:
    print("You are a teenager.")
elif age <= 59:
    print("You are an adult.")
else:
    print("You are a senior.")

R Code

age <- as.integer(readline("Enter your age: "))

if (age < 0) {
  print("Invalid age")
} else if (age <= 12) {
  print("You are a child.")
} else if (age <= 19) {
  print("You are a teenager.")
} else if (age <= 59) {
  print("You are an adult.")
} else {
  print("You are a senior.")
}

2.2 Loops

Loops allow programs to repeat actions multiple times.

  • ✅ For Loop → Used when the number of iterations is known. The counter is set, the condition is checked, the code executes, then the counter increments until the condition is no longer met.
  • ✅ While Loop → Used when looping should continue as long as the condition is true. If the condition remains valid, the code executes and is checked again until the condition becomes false.
  • ✅ Break → Stops the loop early, immediately exiting the loop without completing all iterations.
  • ✅ Continue → Skips the current iteration without stopping the loop, returning directly to the condition check for the next iteration. The loop stops when the condition is no longer met or a break statement is used. The loop continues if the condition remains true unless a break occurs.

2.2.1 Simple Example

Print numbers from 1 to 5 using a for loop.

Python Code

for i in range(1, 6):
    print(i)
1
2
3
4
5

R Code

for (i in 1:5) {
  print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

2.2.2 Medium Example

Using a while loop to print numbers up to a limit.

Python Code

x = 1
while x <= 5:
    print(x)
    x += 1
1
2
3
4
5

R Code

x <- 1
while (x <= 5) {
  print(x)
  x <- x + 1
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

2.2.3 Complex Example

Using break and continue to modify loop behavior.

Python Code

for i in range(1, 11):
    if i == 5:
        continue  # Skip number 5
    if i == 8:
        break     # Stop when i = 8
    print(i)
1
2
3
4
6
7

R Code

for (i in 1:10) {
  if (i == 5) {
    next         # Skip number 5
  }
  if (i == 8) {
    break        # Stop when i = 8
  }
  print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 6
[1] 7

2.3 Error Handling

Error handling allows a program to recover from unexpected issues rather than terminating abruptly.

  • ✅ try-except (Python) → Captures errors and ensures the program continues running.
  • ✅ tryCatch (R) → Provides similar functionality in R, allowing controlled error management.

2.3.1 Simple Example

Handling incorrect date formats during user input

Python Code

from datetime import datetime

try:
    date_str = input("Enter date (YYYY-MM-DD): ")  # Input may have wrong format
    date_obj = datetime.strptime(date_str, "%Y-%m-%d")
    print(f"Entered date: {date_obj}")
except ValueError:
    print("Error: Date format must be YYYY-MM-DD!")

R Code

safe_date <- function() {
  date_str <- readline("Enter date (YYYY-MM-DD): ")
  tryCatch({
    date_obj <- as.Date(date_str, format="%Y-%m-%d")
    if (is.na(date_obj)) stop("Incorrect date format!")
    print(paste("Entered date:", date_obj))
  }, error = function(e) {
    print(paste("Error:", e$message))
  })
}

safe_date()
Enter date (YYYY-MM-DD): 
[1] "Error: Incorrect date format!"

2.3.2 Medium Example

Handling missing values and invalid dates in datasets.

Python Code

# inport library
import pandas as pd

# dataset example (this is dictionary type)
data = {"event": ["A", "B", "C"], "date": ["2024-01-15", "2024-15-10", None]}
df = pd.DataFrame(data)

# error handaling program
try:
    df["date"] = pd.to_datetime(df["date"], errors="coerce")  # dates to NaT
    df.dropna(subset=["date"], inplace=True)  # Remove rows with invalid dates
    print(df)
except Exception as e:
    print(f"Error processing dates: {e}")

R Code

library(dplyr)
library(lubridate)

# dataset example (this is dictionary type)
data <- data.frame(event = c("A", "B", "C"), 
                   date = c("2024-01-15", "2024-15-10", NA))

# error handaling program
safe_process_dates <- function(df) {
  tryCatch({
    df <- df %>%
      mutate(date = ymd(date)) %>%  # Convert dates
      filter(!is.na(date))          # Remove invalid dates
    
    print(df)
  }, error = function(e) {
    print(paste("Error processing dates:", e$message))
  })
}

safe_process_dates(data)

2.3.3 Complex Example

This Python code replicates the R functionality while improving date handling.

Python Code

import pandas as pd
from dateutil import parser
import warnings

# Example dataset with various incorrect date formats
data = pd.DataFrame({
    "event": ["A", "B", "C", "D", "E"],
    "date": ["2024-01-15", "2024-15-10", None, "15-03-2024", "2024/04/05"]
})

def safe_process_dates(df):
    def parse_date(date_str):
        try:
            return parser.parse(date_str, dayfirst=True).strftime("%d-%m-%Y")
        except (ValueError, TypeError):
            return None

    # Apply date parsing
    df["parsed_date"] = df["date"].apply(parse_date)
    
    # Find invalid dates
    invalid_dates = df[df["parsed_date"].isna()]["date"].dropna().tolist()
    
    if invalid_dates:
        warnings.warn(f"Some dates are invalid: {', '.join(invalid_dates)}")
    
    # Filter out invalid dates
    df = df.dropna(subset=["parsed_date"])
    
    print(df)

safe_process_dates(data)
  event        date parsed_date
0     A  2024-01-15  15-01-2024
1     B  2024-15-10  15-10-2024
3     D  15-03-2024  15-03-2024
4     E  2024/04/05  04-05-2024

R Code

library(dplyr)
library(lubridate)

# Example dataset with various incorrect date formats
data <- data.frame(event = c("A", "B", "C", "D", "E"), 
                   date = c("2024-01-15", "2024-15-10", NA, 
                            "15-03-2024", "2024/04/05"))

# Function to handle errors in date conversion
safe_process_dates <- function(df) {
  tryCatch({
    df <- df %>%
      mutate(
        parsed_date = parse_date_time(date, 
                                      orders = c("ymd", "dmy", "mdy", "Y/m/d"),
                                      quiet = TRUE)
      ) %>%
      filter(!is.na(parsed_date)) %>%   # Remove dates that failed to convert
      mutate(formatted_date = format(parsed_date, "%d-%m-%Y")) # Reformat dates

    # Check if there are any invalid dates
    invalid_dates <- df$date[is.na(df$parsed_date)]
    if (length(invalid_dates) > 0) {
      warning("Some dates are invalid: ", paste(invalid_dates, collapse = ", "))
    }
    
    print(df)
  }, error = function(e) {
    print(paste("Error processing dates:", e$message))
  })
}

safe_process_dates(data)
  event       date parsed_date formatted_date
1     A 2024-01-15  2024-01-15     15-01-2024
2     D 15-03-2024  2024-03-15     15-03-2024
3     E 2024/04/05  2024-04-05     05-04-2024

Explanation:

  • dateutil.parser.parse() is used for flexible date recognition.
  • ✅ Handles multiple date formats including YYYY-MM-DD, DD-MM-YYYY, YYYY/MM/DD, etc.
  • ✅ Removes invalid dates and displays warnings for them.
  • ✅ Formats valid dates to YYYY-MM-DD for consistency.

2.4 Best Practices

In this section you will learn the Best Practices about Syntax & Control Flow in Python and R:

2.4.1 Readability & Formatting

  • Follow PEP 8 for clean and readable code.
  • Use consistent indentation (4 spaces per level).
  • Keep line length ≤ 79 characters.
  • Use meaningful variable and function names.

Python Code

# ✅ Good: Readable and follows PEP 8
def calculate_total(price, tax_rate):
    "Calculate the total price after tax."
    total = price + (price * tax_rate)
    return total
# ❌ Bad: Poor formatting and unclear naming
def calc(p, t): return p+(p*t) # One-liner (not recommended)

R Code

# ✅ Good: Readable and follows conventions
total_price <- function(price, tax_rate) {
  "Calculate the total price after tax."
  total <- price + (price * tax_rate)
  return(total)
}
# ❌ Bad: Poor formatting and unclear naming
total <- function(p, t) { p + (p * t) } # One-liner (not recommended)

2.4.2 Efficient Control Flow

  • Use if-elif-else correctly to avoid redundant checks.
  • Avoid deep nesting; use guard clauses to improve readability.

Python Code

# ✅ Good: Uses guard clause to return early
def check_access(user):
    if not user.is_authenticated:
        return "Access Denied"
    
    if user.is_admin:
        return "Access Granted: Admin"

    return "Access Granted: User"
# ❌ Bad: Deeply nested structure
def check_access(user):
    if user.is_authenticated:
        if user.is_admin:
            return "Access Granted: Admin"
        else:
            return "Access Granted: User"
    else:
        return "Access Denied"

R Code

# ✅ Good: Uses early return
check_access <- function(user) {
  if (!user$authenticated) {
    return("Access Denied")
  }
  
  if (user$admin) {
    return("Access Granted: Admin")
  }
  
  return("Access Granted: User")
}

2.4.3 Looping Best Practices

  • Use list comprehensions for simple loops.
  • Use enumerate() instead of manually managing indexes.
  • Use zip() for iterating over multiple lists simultaneously.

Python Code

# ✅ Good: List comprehension for efficiency
squares = [x**2 for x in range(10) if x % 2 == 0]

# ✅ Good: Using enumerate
names = ["Alice", "Bob", "Charlie"]
for index, name in enumerate(names, start=1):
    print(f"{index}: {name}")

# ✅ Good: Using zip()
keys = ["name", "age", "city"]
values = ["Alice", 25, "New York"]
person = dict(zip(keys, values))
# ✅ Good: Vectorized operation
squares <- (0:9)^2

# ✅ Good: Using sapply
names <- c("Alice", "Bob", "Charlie")
print(sapply(seq_along(names), function(i) paste(i, names[i])))
[1] "1 Alice"   "2 Bob"     "3 Charlie"

2.4.4 Common Mistakes in Loops

  • Don’t modify lists while iterating (use a copy instead).
  • Use break and continue sparingly to maintain readability.

Python Code

# ✅ Good: Iterating over a copy when modifying a list
numbers = [1, 2, 3, 4, 5]
for num in numbers[:]:  # Copy of the list
    if num % 2 == 0:
        numbers.remove(num)
# ❌ Bad: Modifying a list while iterating (can cause unexpected behavior)
for num in numbers:
    if num % 2 == 0:
        numbers.remove(num)

R Code

# ✅ Good: Using which to filter elements
numbers <- c(1, 2, 3, 4, 5)
numbers <- numbers[numbers %% 2 != 0]

2.4.5 Cleaner Conditionals

Use match-case instead of long if-elif chains when checking specific values Python 3.10+.

Python

# ✅ Good: Using match-case
def get_status(code):
    match code:
        case 200:
            return "OK"
        case 404:
            return "Not Found"
        case 500:
            return "Internal Server Error"
        case _:
            return "Unknown Status"
# ❌ Bad: Using multiple if-elif
def get_status(code):
    if code == 200:
        return "OK"
    elif code == 404:
        return "Not Found"
    elif code == 500:
        return "Internal Server Error"
    else:
        return "Unknown Status"

R Code

# ✅ Good: Using switch
get_status <- function(code) {
  switch(as.character(code),
         "200" = "OK",
         "404" = "Not Found",
         "500" = "Internal Server Error",
         "Unknown Status")
}

2.4.6 Error Handling

  • Catch only specific exceptions instead of using a general except clause.
  • Use finally for cleanup operations (e.g., closing files, releasing resources).

Python Code

# ✅ Good: Handling specific exceptions
try:
    number = int(input("Enter a number: "))
    result = 10 / number
except ValueError:
    print("Invalid input! Please enter a number.")
except ZeroDivisionError:
    print("Cannot divide by zero.")
finally:
    print("Execution complete.")

R Code

# ✅ Good: Handling specific exceptions
safe_divide <- function(a, b) {
  tryCatch({
    result <- a / b
    return(result)
  }, warning = function(w) {
    message("Warning: ", w$message)
  }, error = function(e) {
    message("Error: Cannot divide by zero.")
  })
}

2.4.7 Resource Management

Use with statements when working with files to ensure proper cleanup.

Python Code

# ✅ Good: Using with statement (auto-closes file)
with open("data.txt", "r") as file:
    content = file.read()
# ❌ Bad: Forgetting to close the file
file = open("data.txt", "r")
content = file.read()
file.close()

R Code

# ✅ Good: Using on.exit() to clean up
read_data <- function(file) {
  con <- file(file, "r")
  on.exit(close(con))
  data <- readLines(con)
  return(data)
}

2.4.8 Mutable Default Arguments

Use immutable types (e.g., None) as default arguments instead of mutable ones.

Python Code

# ✅ Good: Using None to avoid unintended behavior
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

R Code

# ✅ Good: Using NULL to avoid unintended behavior
add_item <- function(item, items = NULL) {
  if (is.null(items)) {
    items <- list()
  }
  items <- append(items, item)
  return(items)
}

2.4.9 Using Generators for

Use yield for efficient memory usage when handling large datasets.

Python Code

# ✅ Good: Using generator function
def count_up_to(n):
    count = 1
    while count <= n:
        yield count
        count += 1

for num in count_up_to(5):
    print(num)

R Code

# ✅ Good: Using closures to generate a sequence
count_up_to <- function(n) {
  count <- 1
  function() {
    if (count <= n) {
      result <- count
      count <<- count + 1
      return(result)
    } else {
      return(NULL)
    }
  }
}

counter <- count_up_to(5)
while (!is.null(value <- counter())) {
  print(value)
}

By following these best practices, you can write clean, efficient, and maintainable Python and R code. 🚀

2.5 Practicum

Independent Practice: Conditional Statements and Loops in Python & R

2.5.1 Objective

  1. Understand and implement conditional statements (if, if-else, if-elif-else).
  2. Apply loops (for loop, while loop, break, continue) to analyze a dataset.

Use the following dummy dataset:

ID Name Age Salary Position Performance
1 Bagas 25 5000 Staff Good
2 Joan 30 7000 Supervisor Very Good
3 Alya 27 6500 Staff Average
4 Dwi 35 10000 Manager Good
5 Nabil 40 12000 Director Very Good

2.5.2 Conditional Statements

Determine bonus levels based on employee performance:

  • Very Good → 20% of salary
  • Good → 10% of salary
  • Average → 5% of salary

Your Task:

  • Write a program in Python and R to calculate each employee’s bonus.
  • Display the output in this format:
    "Name: Bagas, Bonus: 500"

2.5.3 Loops (For & While)

  1. Use a for loop to list employees with a salary greater than 6000.

Expected Output:

Name: Joan, Salary: 7000  
Name: Alya, Salary: 6500  
Name: Dwi, Salary: 10000  
Name: Nabil, Salary: 12000
  1. Use a while loop to display employees until a “Manager” is found.

Expected Output:

Name: Bagas, Position: Staff  
Name: Joan, Position: Supervisor  
Name: Alya, Position: Staff  
Name: Dwi, Position: Manager (Stop here)  
  1. Use break to stop the loop when an employee with a salary above 10,000 is found.

Expected Output:

Name: Bagas, Salary: 5000  
Name: Joan, Salary: 7000  
Name: Alya, Salary: 6500  
Name: Dwi, Salary: 10000  
(Stopped because Nabil has a salary above 10,000)  
  1. Use continue to skip employees with “Average” performance.

Expected Output:

Name: Bagas, Performance: Good  
Name: Joan, Performance: Very Good  
Name: Dwi, Performance: Good  
Name: Nabil, Performance: Very Good  
(Alya is skipped because the performance is "Average")  

Submission Guidelines:

  1. Submit your Python and R code using your Google Colab and Rpubs.
  2. Ensure the output is displayed correctly.
  3. Add comments in the code to explain your logic.