R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
189: Function Factories in R
You've likely written plenty of functions that take arguments and return values. But as your R projects grow, you'll find yourself writing the same function over and over, just changing one constant value. For example, maybe you need five different functions to calculate tax for five different states, or three different scaling functions for different datasets.
The instinct for many is to use a loop to generate these functions. It seems efficient, but R has a quirk called "lazy evaluation" that often bites people right here. Take a look at this attempt to create a set of "power" functions.
# I want to create functions: pow2, pow3, pow4, pow5
funcs <- list()
for (i in 2:5) {
funcs[[paste0("pow", i)]] <- function(x) {
x^i
}
}
# Now let's test them
funcs$pow2(10) # Expected 100
funcs$pow3(10) # Expected 1000
The Lazy Evaluation Trap
If you run that code, you'll notice something unsettling: funcs$pow2(10) doesn't return 100. It returns 100,000. In fact, every single function in that list returns 10^5. Why?
In R, the inner function doesn't "grab" the value of i at the moment the function is created. Instead, it remembers that it needs to look for a variable named i in the surrounding environment. By the time you actually call funcs$pow2(10), the loop has already finished, and the value of i in the environment is 5. Every function is looking at the same i, which is now 5.
Capturing State with Closures
To fix this, we need a Function Factory. A factory is simply a function that returns another function. The key is that the inner function "closes over" the environment of the outer function. This creates a "closure," effectively freezing the value of the argument at the moment the factory was called.
# This is our factory
make_power_func <- function(exponent) {
# This inner function is what gets returned
function(x) {
x^exponent
}
}
# Now we create our specific functions
pow2 <- make_power_func(2)
pow3 <- make_power_func(3)
pow2(10) # Returns 100
pow3(10) # Returns 1000
Here's what happened: when we called make_power_func(2), R created a unique environment for that specific call where exponent was 2. The function returned by the factory carries that environment with it. When you call pow2(10), it looks back at its original birthplace and finds exponent = 2, regardless of what's happening elsewhere in your script.
When to Actually Use Factories
I'll be honest: if you only need two versions of a function, just write two functions. But factories are incredibly powerful when you're building APIs or packages. They allow you to create "configurable" behavior. Instead of passing a configuration parameter into a function every single time you call it in a loop, you configure the function once using the factory, and then pass that specialized function into other higher-order functions like lapply() or purrr::map().
📋 Practical Task
Building a Custom Currency Converter Factory
Imagine you are building a financial reporting tool. You need several functions that convert various currencies into USD, but the exchange rates change daily. Instead of hard-coding rates into every function, you will build a factory.
Your Task:
- Create a function factory called
make_converterthat takes one argument:rate(the value of 1 unit of foreign currency in USD). - The factory should return a function that takes a
amountargument and returns the converted USD value (amount * rate). - Use your factory to create two specific functions:
euro_to_usd(using a rate of 1.08) andgbp_to_usd(using a rate of 1.27). - Test both functions to ensure they return the correct values for an input of 100.
There are no comments for now.