Introduction to Functions
Functions are the building blocks of modern programming. They allow us to organize code into reusable, self-contained units that perform specific tasks. Understanding functions is fundamental to writing clean, maintainable, and efficient code across any programming language.
Key Concepts
- Reusability: Write once, use many times
- Abstraction: Hide complex implementation details
- Modularity: Break complex problems into smaller pieces
- Encapsulation: Group related operations together
- Separation of Concerns: Each function does one thing well
1. What Are Functions?
Basic Concept
A function is a block of organized, reusable code that performs a specific task. Functions take inputs (parameters), process them, and optionally return outputs.
// Simple function analogy
// Think of a function like a blender:
// Input: fruits (parameters)
// Process: blend (function body)
// Output: smoothie (return value)
function makeSmoothie(fruits) {
// Process the fruits
let smoothie = blend(fruits);
return smoothie; // Output
}
Function Components
# All functions have these components: def calculate_area(length, width): # 1. Function name and parameters """Calculate area of rectangle""" # 2. Documentation (docstring) area = length * width # 3. Function body (implementation) return area # 4. Return value (optional) # Components explained: # - def: Function definition keyword # - calculate_area: Function name (descriptive) # - length, width: Parameters (inputs) # - docstring: Documentation # - area = length * width: Function logic # - return: Output value
2. Why Use Functions?
Code Reusability
// Without functions - Code duplication
// Calculate area of first rectangle
let rect1Length = 5;
let rect1Width = 3;
let area1 = rect1Length * rect1Width;
console.log(`Area 1: ${area1}`);
// Calculate area of second rectangle
let rect2Length = 7;
let rect2Width = 4;
let area2 = rect2Length * rect2Width;
console.log(`Area 2: ${area2}`);
// With functions - DRY (Don't Repeat Yourself)
function calculateArea(length, width) {
return length * width;
}
console.log(`Area 1: ${calculateArea(5, 3)}`);
console.log(`Area 2: ${calculateArea(7, 4)}`);
Modularity and Organization
# Breaking down complex problems
def get_user_input():
"""Get user's name and age"""
name = input("Enter name: ")
age = int(input("Enter age: "))
return name, age
def validate_age(age):
"""Check if age is valid"""
return age >= 0 and age <= 150
def generate_greeting(name, age):
"""Create personalized greeting"""
if age < 18:
return f"Hi {name}! You're a young star!"
else:
return f"Hello {name}! Welcome to the club!"
def main():
"""Main program flow"""
name, age = get_user_input()
if validate_age(age):
message = generate_greeting(name, age)
print(message)
else:
print("Invalid age!")
# Run the program
main()
Abstraction
// Hiding complexity behind a simple interface
public class EmailService {
// Complex internal implementation
private void connectToServer() { /* ... */ }
private void authenticate() { /* ... */ }
private void formatMessage(String message) { /* ... */ }
private void sendRaw() { /* ... */ }
private void disconnect() { /* ... */ }
// Simple public interface
public void sendEmail(String recipient, String subject, String body) {
connectToServer();
authenticate();
String formattedMessage = formatMessage(body);
sendRaw();
disconnect();
System.out.println("Email sent to " + recipient);
}
}
// Using the function - user doesn't need to know internal details
EmailService email = new EmailService();
email.sendEmail("[email protected]", "Hello", "How are you?");
3. Function Syntax Across Languages
Python
# Basic function
def greet(name):
return f"Hello, {name}!"
# Function with default parameters
def greet_person(name, greeting="Hello"):
return f"{greeting}, {name}!"
# Function with variable arguments
def sum_all(*args):
return sum(args)
# Function with keyword arguments
def create_user(**kwargs):
return kwargs
# Lambda (anonymous) function
square = lambda x: x * x
# Function annotations (type hints)
def divide(a: float, b: float) -> float:
"""Divide a by b"""
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
# Generator function
def count_up_to(n):
i = 0
while i < n:
yield i
i += 1
# Async function
async def fetch_data():
await asyncio.sleep(1)
return "data"
JavaScript
// Function declaration
function greet(name) {
return `Hello, ${name}!`;
}
// Function expression
const greet = function(name) {
return `Hello, ${name}!`;
};
// Arrow function
const greet = (name) => `Hello, ${name}!`;
// Default parameters
function greet(name = "World") {
return `Hello, ${name}!`;
}
// Rest parameters
function sum(...numbers) {
return numbers.reduce((acc, n) => acc + n, 0);
}
// Immediately Invoked Function Expression (IIFE)
(function() {
console.log("Runs immediately");
})();
// Generator function
function* countUpTo(n) {
for (let i = 0; i < n; i++) {
yield i;
}
}
// Async function
async function fetchData() {
const response = await fetch(url);
return response.json();
}
// Higher-order function
function multiplier(factor) {
return function(x) {
return x * factor;
};
}
const double = multiplier(2);
console.log(double(5)); // 10
Java
// Basic method
public static String greet(String name) {
return "Hello, " + name + "!";
}
// Overloaded methods
public static int add(int a, int b) {
return a + b;
}
public static double add(double a, double b) {
return a + b;
}
// Varargs (variable arguments)
public static int sum(int... numbers) {
int total = 0;
for (int n : numbers) {
total += n;
}
return total;
}
// Generic method
public static <T> T identity(T value) {
return value;
}
// Lambda expression
Function<Integer, Integer> square = x -> x * x;
// Method reference
List<String> names = Arrays.asList("Alice", "Bob");
names.forEach(System.out::println);
// Optional return
public Optional<String> findUser(String id) {
return Optional.ofNullable(userMap.get(id));
}
C/C++
// C function
int add(int a, int b) {
return a + b;
}
// Function prototype (declaration)
int multiply(int a, int b);
// Inline function
static inline int square(int x) {
return x * x;
}
// Function pointer
int (*operation)(int, int);
operation = &add;
int result = operation(5, 3);
// Variadic function
#include <stdarg.h>
int sum(int count, ...) {
va_list args;
va_start(args, count);
int total = 0;
for (int i = 0; i < count; i++) {
total += va_arg(args, int);
}
va_end(args);
return total;
}
// C++ with modern features
#include <iostream>
#include <vector>
#include <algorithm>
// Function overloading
void print(int x) {
std::cout << "Int: " << x << std::endl;
}
void print(double x) {
std::cout << "Double: " << x << std::endl;
}
// Default parameters
void greet(std::string name = "World") {
std::cout << "Hello, " << name << "!" << std::endl;
}
// Template function
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
// Lambda expression
auto square = [](int x) { return x * x; };
// Constexpr function (compile-time)
constexpr int factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
// Move semantics
std::vector<int> createVector() {
std::vector<int> v{1, 2, 3};
return v; // Move semantics, not copy
}
Rust
// Basic function
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
// Function with multiple returns (tuple)
fn divide(a: i32, b: i32) -> (i32, i32) {
(a / b, a % b)
}
// Generic function
fn identity<T>(value: T) -> T {
value
}
// Function with trait bounds
fn print_value<T: std::fmt::Display>(value: T) {
println!("{}", value);
}
// Closure (anonymous function)
let square = |x: i32| x * x;
// Higher-order function
fn apply_twice<F>(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32,
{
f(f(x))
}
// Function returning closure
fn make_adder(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
// Async function
async fn fetch_data() -> String {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
"data".to_string()
}
Go
package main
import "fmt"
// Basic function
func greet(name string) string {
return fmt.Sprintf("Hello, %s!", name)
}
// Multiple return values
func divide(a, b int) (int, int) {
return a / b, a % b
}
// Named return values
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // naked return
}
// Variadic function
func sum(numbers ...int) int {
total := 0
for _, n := range numbers {
total += n
}
return total
}
// Function as value
var operation func(int, int) int
operation = func(a, b int) int { return a + b }
// Method (function with receiver)
type Rectangle struct {
width, height float64
}
func (r Rectangle) area() float64 {
return r.width * r.height
}
// Interface method
type Shape interface {
area() float64
}
// Defer function
func readFile() {
file := openFile()
defer file.Close() // Runs when function returns
// Process file...
}
4. Function Parameters
Pass by Value vs Pass by Reference
// JavaScript - Primitives are passed by value
function modifyPrimitive(x) {
x = 10; // This doesn't affect the original
}
let a = 5;
modifyPrimitive(a);
console.log(a); // Still 5
// Objects are passed by reference
function modifyObject(obj) {
obj.value = 10; // This modifies the original
}
let myObj = { value: 5 };
modifyObject(myObj);
console.log(myObj.value); // Now 10
# Python - Everything is passed by object reference def modify_list(lst): lst.append(4) # Modifies the original list def reassign_list(lst): lst = [1, 2, 3] # Does NOT affect original my_list = [1, 2, 3] modify_list(my_list) print(my_list) # [1, 2, 3, 4] reassign_list(my_list) print(my_list) # Still [1, 2, 3, 4]
// C++ - Multiple parameter passing methods
#include <iostream>
using namespace std;
// Pass by value (copied)
void byValue(int x) {
x = 10; // Changes only the copy
}
// Pass by reference
void byReference(int& x) {
x = 10; // Changes the original
}
// Pass by pointer
void byPointer(int* x) {
*x = 10; // Changes the original
}
// Pass by const reference (read-only)
void print(const int& x) {
cout << x << endl; // Cannot modify
}
int main() {
int a = 5;
byValue(a);
cout << a << endl; // 5
byReference(a);
cout << a << endl; // 10
byPointer(&a);
cout << a << endl; // 10
return 0;
}
Default Parameters
# Python default parameters
def greet(name="World", greeting="Hello"):
return f"{greeting}, {name}!"
print(greet()) # Hello, World!
print(greet("Alice")) # Hello, Alice!
print(greet("Bob", "Hi")) # Hi, Bob!
// JavaScript default parameters
function greet(name = "World", greeting = "Hello") {
return `${greeting}, ${name}!`;
}
console.log(greet()); // Hello, World!
console.log(greet("Alice")); // Hello, Alice!
console.log(greet("Bob", "Hi")); // Hi, Bob!
// Java - Use method overloading for default parameters
public class Greeting {
public static String greet() {
return greet("World", "Hello");
}
public static String greet(String name) {
return greet(name, "Hello");
}
public static String greet(String name, String greeting) {
return greeting + ", " + name + "!";
}
}
Keyword/Named Parameters
# Python keyword arguments
def create_profile(name, age, city="Unknown", country="USA"):
return f"{name}, {age}, from {city}, {country}"
# Can specify arguments in any order
profile = create_profile(age=30, name="Alice", country="Canada")
// JavaScript - Use object destructuring for named parameters
function createProfile({ name, age, city = "Unknown", country = "USA" }) {
return `${name}, ${age}, from ${city}, ${country}`;
}
const profile = createProfile({ name: "Alice", age: 30, country: "Canada" });
5. Return Values
Single Return Value
def add(a, b): return a + b result = add(5, 3) print(result) # 8
Multiple Return Values
# Python - Return tuple
def get_min_max(numbers):
return min(numbers), max(numbers)
minimum, maximum = get_min_max([1, 2, 3, 4, 5])
print(f"Min: {minimum}, Max: {maximum}")
// JavaScript - Return array or object
function getMinMax(numbers) {
return [Math.min(...numbers), Math.max(...numbers)];
}
const [min, max] = getMinMax([1, 2, 3, 4, 5]);
console.log(`Min: ${min}, Max: ${max}`);
// Go - Multiple returns are built-in
func divide(a, b int) (int, int) {
return a / b, a % b
}
quotient, remainder := divide(17, 5)
fmt.Printf("Quotient: %d, Remainder: %d", quotient, remainder)
Optional Returns
# Python - Optional return (returns None by default)
def print_message(msg):
print(msg)
# Implicitly returns None
result = print_message("Hello")
print(result) # None
// JavaScript - Returns undefined by default
function logMessage(msg) {
console.log(msg);
}
let result = logMessage("Hello");
console.log(result); // undefined
Error Handling Returns
# Python - Return None or raise exception
def safe_divide(a, b):
if b == 0:
return None
return a / b
# Or use exception handling
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
// Go - Return error as second value
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
// JavaScript - Throw exception or return special value
function divide(a, b) {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
}
// Or return null/undefined
function safeDivide(a, b) {
if (b === 0) return null;
return a / b;
}
6. Function Scope
Local vs Global Scope
# Python scope global_var = 10 def my_function(): local_var = 20 print(global_var) # Can access global # print(local_var) # Would work here # return local_var my_function() # print(local_var) # Error: local_var not defined outside
// JavaScript scope
let globalVar = 10;
function myFunction() {
let localVar = 20;
console.log(globalVar); // Can access global
// Inner function has access to outer variables
function inner() {
console.log(localVar); // Can access
console.log(globalVar); // Can access
}
inner();
}
myFunction();
// console.log(localVar); // Error: localVar not defined
Closures
// JavaScript closure - function that remembers its scope
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
# Python closure def create_counter(): count = 0 def counter(): nonlocal count count += 1 return count return counter counter = create_counter() print(counter()) # 1 print(counter()) # 2 print(counter()) # 3
Lexical Scope
// JavaScript lexical scoping
let name = "Global";
function outer() {
let name = "Outer";
function inner() {
let name = "Inner";
console.log(name); // "Inner"
}
inner();
console.log(name); // "Outer"
}
outer();
console.log(name); // "Global"
7. Higher-Order Functions
Functions as Arguments
// Passing functions as arguments
function applyOperation(a, b, operation) {
return operation(a, b);
}
const add = (x, y) => x + y;
const multiply = (x, y) => x * y;
console.log(applyOperation(5, 3, add)); // 8
console.log(applyOperation(5, 3, multiply)); // 15
// Array methods use higher-order functions
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, n) => acc + n, 0);
# Python - Functions as arguments def apply_operation(a, b, operation): return operation(a, b) def add(x, y): return x + y def multiply(x, y): return x * y print(apply_operation(5, 3, add)) # 8 print(apply_operation(5, 3, multiply)) # 15 # Built-in higher-order functions numbers = [1, 2, 3, 4, 5] doubled = list(map(lambda x: x * 2, numbers)) evens = list(filter(lambda x: x % 2 == 0, numbers)) sum_all = sum(numbers)
Functions Returning Functions
// Factory function
function createGreeting(greeting) {
return function(name) {
return `${greeting}, ${name}!`;
};
}
const sayHello = createGreeting("Hello");
const sayHi = createGreeting("Hi");
console.log(sayHello("Alice")); // Hello, Alice!
console.log(sayHi("Bob")); // Hi, Bob!
# Python - Function factory
def create_greeting(greeting):
def greet(name):
return f"{greeting}, {name}!"
return greet
say_hello = create_greeting("Hello")
say_hi = create_greeting("Hi")
print(say_hello("Alice")) # Hello, Alice!
print(say_hi("Bob")) # Hi, Bob!
Composition
// Function composition const compose = (f, g) => (x) => f(g(x)); const add1 = (x) => x + 1; const multiply2 = (x) => x * 2; const add1ThenMultiply2 = compose(multiply2, add1); console.log(add1ThenMultiply2(5)); // (5 + 1) * 2 = 12
# Python - Function composition def compose(f, g): return lambda x: f(g(x)) def add1(x): return x + 1 def multiply2(x): return x * 2 add1_then_multiply2 = compose(multiply2, add1) print(add1_then_multiply2(5)) # 12
8. Recursion
Basic Recursion
# Factorial using recursion def factorial(n): # Base case if n <= 1: return 1 # Recursive case return n * factorial(n - 1) print(factorial(5)) # 120 # Fibonacci using recursion def fibonacci(n): if n <= 1: return n return fibonacci(n - 1) + fibonacci(n - 2) print(fibonacci(10)) # 55
// JavaScript recursion
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
console.log(factorial(5)); // 120
Tail Recursion
# Tail recursion optimization (Python doesn't optimize) def factorial_tail(n, accumulator=1): if n <= 1: return accumulator return factorial_tail(n - 1, n * accumulator) print(factorial_tail(5)) # 120
// JavaScript (some engines optimize tail calls)
function factorialTail(n, accumulator = 1) {
if (n <= 1) return accumulator;
return factorialTail(n - 1, n * accumulator);
}
Recursion vs Iteration
# Recursive vs iterative approaches # Recursive tree traversal def traverse_tree(node): if node is None: return print(node.value) traverse_tree(node.left) traverse_tree(node.right) # Iterative solution using stack def traverse_tree_iterative(root): stack = [root] while stack: node = stack.pop() if node: print(node.value) stack.append(node.right) stack.append(node.left)
9. Pure vs Impure Functions
Pure Functions
// Pure functions - always return same output for same input
// No side effects
function add(a, b) {
return a + b; // Pure: no external state, no side effects
}
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}
// Deterministic and referentially transparent
const result = add(5, 3); // Always 8
# Python pure functions def add(a, b): return a + b def square(x): return x * x # Benefits of pure functions: # - Easier to test # - Easier to debug # - Can be memoized # - Thread-safe
Impure Functions
// Impure functions - have side effects or depend on external state
let count = 0;
function increment() {
count++; // Modifies external state (side effect)
return count;
}
function getRandomNumber() {
return Math.random(); // Different output each time
}
function logMessage(message) {
console.log(message); // Side effect (I/O)
}
10. Functional Programming Patterns
Map, Filter, Reduce
// Functional transformation of data const numbers = [1, 2, 3, 4, 5]; // Map: transform each element const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10] // Filter: select elements based on condition const evens = numbers.filter(n => n % 2 === 0); // [2, 4] // Reduce: accumulate values const sum = numbers.reduce((acc, n) => acc + n, 0); // 15 // Chaining const result = numbers .filter(n => n % 2 === 0) .map(n => n * n) .reduce((acc, n) => acc + n, 0); // 4*4 + 2*2 = 16 + 4 = 20
# Python functional operations from functools import reduce numbers = [1, 2, 3, 4, 5] # Map doubled = list(map(lambda x: x * 2, numbers)) # Filter evens = list(filter(lambda x: x % 2 == 0, numbers)) # Reduce sum_all = reduce(lambda acc, x: acc + x, numbers, 0) # List comprehension (more Pythonic) squared_evens = [x * x for x in numbers if x % 2 == 0]
Currying
// Currying - transforming function with multiple arguments into sequence of single-argument functions
function multiply(a) {
return function(b) {
return a * b;
};
}
const multiplyBy2 = multiply(2);
console.log(multiplyBy2(5)); // 10
// Using arrow functions
const multiply = a => b => a * b;
const double = multiply(2);
console.log(double(5)); // 10
# Python currying with functools from functools import partial def multiply(a, b): return a * b double = partial(multiply, 2) print(double(5)) # 10
11. Async Functions
Callback Pattern
// Traditional callback pattern
function fetchData(callback) {
setTimeout(() => {
callback("Data received");
}, 1000);
}
fetchData((data) => {
console.log(data);
});
Promises
// Promise-based functions
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Data received");
}, 1000);
});
}
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
Async/Await
// Modern async/await
async function getData() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error(error);
}
}
getData();
# Python asyncio import asyncio async def fetch_data(): await asyncio.sleep(1) return "Data received" async def main(): data = await fetch_data() print(data) asyncio.run(main())
12. Best Practices
Function Design Principles
# Single Responsibility Principle
# Good: One function does one thing
def validate_email(email):
return '@' in email and '.' in email
def send_welcome_email(email):
# Send email logic
pass
def register_user(username, email):
if not validate_email(email):
raise ValueError("Invalid email")
# Create user logic
send_welcome_email(email)
# Bad: Function does too much
def register_and_notify(username, email):
if '@' not in email or '.' not in email:
return "Invalid email"
# Create user
# Send email
# Log action
# Return result
pass
Naming Conventions
# Good function names - descriptive def calculate_average(numbers): pass def is_valid_email(email): pass def get_user_by_id(user_id): pass # Bad function names - vague def calc(data): pass def check(x): pass def do_stuff(): pass
Documentation
# Python docstrings
def calculate_discount(price, discount_percent):
"""
Calculate discounted price.
Args:
price (float): Original price
discount_percent (float): Discount percentage (0-100)
Returns:
float: Discounted price
Raises:
ValueError: If discount_percent is outside 0-100
"""
if not 0 <= discount_percent <= 100:
raise ValueError("Discount must be between 0 and 100")
return price * (100 - discount_percent) / 100
/**
* Calculate discounted price
* @param {number} price - Original price
* @param {number} discountPercent - Discount percentage (0-100)
* @returns {number} Discounted price
* @throws {Error} If discountPercent is out of range
*/
function calculateDiscount(price, discountPercent) {
if (discountPercent < 0 || discountPercent > 100) {
throw new Error("Discount must be between 0 and 100");
}
return price * (100 - discountPercent) / 100;
}
Testing Functions
# Unit testing functions import unittest def add(a, b): return a + b class TestMathFunctions(unittest.TestCase): def test_add(self): self.assertEqual(add(2, 3), 5) self.assertEqual(add(-1, 1), 0) self.assertEqual(add(0, 0), 0) if __name__ == '__main__': unittest.main()
// Jest testing
function add(a, b) {
return a + b;
}
test('adds 2 + 3 to equal 5', () => {
expect(add(2, 3)).toBe(5);
});
test('adds negative numbers', () => {
expect(add(-1, 1)).toBe(0);
});
13. Common Patterns
Factory Pattern
// Factory function pattern
function createUser(name, age) {
return {
name,
age,
greet() {
return `Hello, I'm ${name}`;
},
isAdult() {
return age >= 18;
}
};
}
const user1 = createUser("Alice", 25);
const user2 = createUser("Bob", 16);
Module Pattern
// Module pattern using IIFE
const Calculator = (function() {
// Private variables
let result = 0;
// Private function
function validateNumber(n) {
return typeof n === 'number' && !isNaN(n);
}
// Public API
return {
add: function(x) {
if (validateNumber(x)) {
result += x;
}
return this;
},
subtract: function(x) {
if (validateNumber(x)) {
result -= x;
}
return this;
},
getResult: function() {
return result;
},
reset: function() {
result = 0;
return this;
}
};
})();
Calculator.add(5).subtract(2).add(10);
console.log(Calculator.getResult()); // 13
Strategy Pattern
// Strategy pattern with functions
const strategies = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b
};
function calculate(strategy, a, b) {
if (!strategies[strategy]) {
throw new Error(`Unknown strategy: ${strategy}`);
}
return strategies[strategy](a, b);
}
console.log(calculate('add', 5, 3)); // 8
console.log(calculate('multiply', 5, 3)); // 15
14. Performance Considerations
Function Call Overhead
// Inline code vs function call performance
// For simple operations, inline code can be faster
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i; // Inline
}
// Versus function call
function add(a, b) {
return a + b;
}
let sum2 = 0;
for (let i = 0; i < 1000000; i++) {
sum2 = add(sum2, i); // Function call overhead
}
Memoization
# Memoization - caching function results from functools import lru_cache @lru_cache(maxsize=128) def fibonacci(n): if n <= 1: return n return fibonacci(n - 1) + fibonacci(n - 2) # First call - calculates print(fibonacci(30)) # 832040 # Second call - returns cached result instantly print(fibonacci(30)) # 832040
// JavaScript memoization
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
};
}
const fibonacci = memoize((n) => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
Inlining
# Inline small functions for performance # Python can use function annotations for optimization hints def process_item(item): return item * 2 # For performance-critical code, inline the operation result = [item * 2 for item in items] # Faster than calling function in loop
15. Real-World Examples
Data Processing Pipeline
# Functional data processing pipeline
def read_data(filename):
"""Read raw data from file"""
with open(filename, 'r') as f:
return f.readlines()
def clean_data(lines):
"""Remove whitespace and empty lines"""
return [line.strip() for line in lines if line.strip()]
def parse_data(lines):
"""Parse lines into structured data"""
return [line.split(',') for line in lines]
def transform_data(data):
"""Transform data to desired format"""
return [{'name': row[0], 'value': int(row[1])} for row in data]
def filter_data(data, threshold=100):
"""Filter data based on threshold"""
return [item for item in data if item['value'] > threshold]
def aggregate_data(data):
"""Aggregate data"""
return sum(item['value'] for item in data)
# Pipeline composition
def process_pipeline(filename):
return (filename
|> read_data
|> clean_data
|> parse_data
|> transform_data
|> (lambda d: filter_data(d, 50))
|> aggregate_data
)
Web API Handler
// Express.js style route handlers
const handlers = {
getUser: async (req, res) => {
const userId = req.params.id;
const user = await db.findUser(userId);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
return res.json({ data: user });
},
createUser: async (req, res) => {
const userData = req.body;
const validation = validateUserData(userData);
if (!validation.valid) {
return res.status(400).json({ errors: validation.errors });
}
const user = await db.createUser(userData);
return res.status(201).json({ data: user });
}
};
// Middleware pattern
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
};
// Compose middleware
const protectedRoute = [authMiddleware, handlers.getUser];
Error Handling Wrapper
# Decorator for error handling
def handle_errors(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except ValueError as e:
print(f"Value error: {e}")
return None
except KeyError as e:
print(f"Key error: {e}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
raise
return wrapper
@handle_errors
def parse_config(config_path):
with open(config_path) as f:
return json.load(f)
@handle_errors
def process_data(data):
return data['value'] / data['divisor']
Conclusion
Functions are the foundation of structured programming:
Key Takeaways
- Reusability: Write once, use many times
- Modularity: Break complex problems into manageable pieces
- Abstraction: Hide complexity behind simple interfaces
- Testability: Functions can be tested in isolation
- Maintainability: Easier to understand and modify
Function Types Summary
| Type | Description | Example |
|---|---|---|
| Pure | No side effects, deterministic | add(a, b) |
| Impure | Has side effects | log(message) |
| Higher-order | Takes/returns functions | map, filter |
| Recursive | Calls itself | factorial |
| Anonymous | No name, inline | lambda |
| Generator | Yields multiple values | yield |
| Async | Handles async operations | async/await |
Best Practices Checklist
- [ ] Single Responsibility Principle
- [ ] Descriptive naming
- [ ] Proper documentation
- [ ] Error handling
- [ ] Input validation
- [ ] Test coverage
- [ ] Avoid side effects when possible
- [ ] Keep functions small and focused
- [ ] Use meaningful parameter names
- [ ] Handle edge cases
Functions are the building blocks that make complex programs possible. Master them, and you'll write better, more maintainable code in any programming language!