Function Behavior

Back

Loading concept...

R Function Behavior: Scope, Nesting & Recursion

The Magic Treehouse Analogy

Imagine you have a magic treehouse with special rooms. Each room has its own toys, and some rooms are inside other rooms. The treehouse has rules about which toys you can play with depending on which room you’re in!


1. Function Scope: Where Can You Play?

What is Scope?

Scope is like asking: “Which toys can I see from here?”

In R, when you create a variable inside a function, it’s like putting a toy in a room. That toy only exists in THAT room!

The Two Types of Scope

# GLOBAL scope (the big living room)
my_toy <- "teddy bear"

play_room <- function() {
  # LOCAL scope (your bedroom)
  secret_toy <- "robot"
  print(my_toy)      # Can see!
  print(secret_toy)  # Can see!
}

play_room()
# [1] "teddy bear"
# [1] "robot"

print(secret_toy)  # ERROR! Can't see!

Why Does This Happen?

Think of it this way:

  • Global variables = Toys in the living room (everyone can see them)
  • Local variables = Toys in YOUR room (only you can see them)

When you’re in your room, you can look out and see the living room toys. But from the living room, you can’t see inside your closed bedroom!

The Lookup Rule

R looks for variables like a child looking for a toy:

  1. First, check YOUR room (current function)
  2. If not found, check the living room (global environment)
  3. If still not found, check the garage (loaded packages)
x <- 10  # Living room toy

find_x <- function() {
  x <- 5  # Bedroom toy (same name!)
  return(x)
}

find_x()  # Returns 5 (found in bedroom)
print(x)  # Returns 10 (living room)

Modifying Global Variables

Want to change a living room toy from your bedroom? Use the super arrow <<-:

score <- 0  # Global

add_point <- function() {
  score <<- score + 1  # Super arrow!
}

add_point()
add_point()
print(score)  # [1] 2

Warning: Using <<- is like reaching out of your room to move toys around. It works, but it can confuse others!


2. Nested Functions: Rooms Inside Rooms

What Are Nested Functions?

A nested function is like having a closet inside your bedroom, inside the treehouse. It’s a function defined INSIDE another function!

outer_room <- function() {
  message <- "Hello from outer!"

  inner_closet <- function() {
    print(message)  # Can see outer's toys!
    print("Hello from inner!")
  }

  inner_closet()
}

outer_room()
# [1] "Hello from outer!"
# [1] "Hello from inner!"

The Nesting Magic

The inner function can see everything the outer function has:

make_multiplier <- function(n) {
  # n is like a toy in this room

  multiplier <- function(x) {
    return(x * n)  # Uses n from outer!
  }

  return(multiplier)
}

times_three <- make_multiplier(3)
times_three(5)   # [1] 15
times_three(10)  # [1] 30

Why Is This Useful?

Imagine you’re making a special calculator. The outer function sets up the rules, and the inner function does the work:

make_greeter <- function(greeting) {

  greet <- function(name) {
    paste(greeting, name, "!")
  }

  return(greet)
}

say_hello <- make_greeter("Hello")
say_howdy <- make_greeter("Howdy")

say_hello("Emma")  # [1] "Hello Emma !"
say_howdy("Jake")  # [1] "Howdy Jake !"

Each greeting function remembers its own special word!

Scope Chain Diagram

graph TD A["Global Environment"] --> B["outer_function"] B --> C["inner_function"] C --> D["innermost_function"] style A fill:#e8f5e9 style B fill:#fff3e0 style C fill:#e3f2fd style D fill:#fce4ec

The innermost function can see ALL the toys from every level above it!


3. Recursive Functions: The Mirror Trick

What Is Recursion?

Imagine standing between two mirrors. You see yourself, and then yourself again, and again, smaller each time until it stops.

A recursive function is a function that calls ITSELF!

The Simplest Example: Countdown

countdown <- function(n) {
  if (n <= 0) {
    print("BLAST OFF!")
    return()
  }

  print(n)
  countdown(n - 1)  # Calls itself!
}

countdown(3)
# [1] 3
# [1] 2
# [1] 1
# [1] "BLAST OFF!"

The Two Magic Rules

Every recursive function needs:

  1. Base Case - When to STOP (the ground floor)
  2. Recursive Case - How to get closer to stopping
factorial <- function(n) {
  # Base case: stop here!
  if (n <= 1) {
    return(1)
  }

  # Recursive case: call myself
  return(n * factorial(n - 1))
}

factorial(5)  # 5 * 4 * 3 * 2 * 1 = 120

How Does It Work?

Think of it like stacking plates:

graph TD A["factorial&#35;40;5&#35;41;"] --> B["5 Ă— factorial&#35;40;4&#35;41;"] B --> C["4 Ă— factorial&#35;40;3&#35;41;"] C --> D["3 Ă— factorial&#35;40;2&#35;41;"] D --> E["2 Ă— factorial&#35;40;1&#35;41;"] E --> F["Returns 1"] F --> G["2 Ă— 1 = 2"] G --> H["3 Ă— 2 = 6"] H --> I["4 Ă— 6 = 24"] I --> J["5 Ă— 24 = 120"]

Real Example: Sum of Numbers

sum_to <- function(n) {
  if (n == 1) {
    return(1)  # Base case
  }
  return(n + sum_to(n - 1))
}

sum_to(4)  # 4 + 3 + 2 + 1 = 10

The Fibonacci Dance

Each Fibonacci number is the sum of the two before it:

fibonacci <- function(n) {
  if (n <= 2) {
    return(1)  # Base case
  }
  return(fibonacci(n-1) + fibonacci(n-2))
}

fibonacci(7)  # [1] 13
# Sequence: 1, 1, 2, 3, 5, 8, 13

Common Recursion Patterns

Pattern Example Base Case
Countdown f(n-1) n <= 0
List walking f(tail) empty list
Tree traversal f(left) + f(right) leaf node
Divide & conquer f(half) size = 1

Putting It All Together

Here’s a beautiful example combining all three concepts:

# Global counter
call_count <- 0

# Outer function with nested helper
make_counter <- function(max) {

  # Nested recursive function
  count_up <- function(current) {
    call_count <<- call_count + 1

    if (current > max) {
      return("Done!")
    }

    print(current)
    count_up(current + 1)
  }

  return(count_up)
}

my_counter <- make_counter(3)
my_counter(1)
# [1] 1
# [1] 2
# [1] 3
# [1] "Done!"

print(call_count)  # [1] 4

Quick Summary

Concept What It Means Remember
Scope Where variables live Rooms in a treehouse
Local Inside function only Your bedroom toys
Global Everywhere Living room toys
Nested Function in function Closet in bedroom
Recursive Calls itself Mirror reflection
Base Case When to stop The ground floor

You Did It!

Now you understand:

  • Why variables can be “invisible” sometimes (scope)
  • How to put functions inside functions (nesting)
  • How functions can call themselves (recursion)

These three concepts are like superpowers. Once you master them, you can solve problems that seemed impossible before!

Pro Tip: When writing recursive functions, ALWAYS write the base case first. It’s like making sure you have a parachute before jumping!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.