Recursive traversals
2023-10-01
In most cases, we can traverse data by loop according to its index; and most languages provide syntatic-sugar for the iteration of such data, such as list comprehension in python and let i in array
grammar in javascript. However, the recursion is needed if you want to traverse unstructured data, such as list in R, dictionary in javascript, and object in javascript.
The recursion on such complex data comonly includes a if else
structure; the first case is your desired behavior on objects’s specified children node; the second is the recursive case that is loop over the data and recursion on each element. a example from modern javascript
let company = { // the same object, compressed for brevity
sales: [{name: 'John', salary: 1000}, {name: 'Alice', salary: 1600 }],
development: {
sites: [{name: 'Peter', salary: 2000}, {name: 'Alex', salary: 1800 }],
internals: [{name: 'Jack', salary: 1300}]
}
};
// The function to do the job
function sumSalaries(department) {
if (Array.isArray(department)) { // case (1)
return department.reduce((prev, current) => prev + current.salary, 0); // sum the array
} else { // case (2)
let sum = 0;
for (let subdep of Object.values(department)) {
sum += sumSalaries(subdep); // recursively call for subdepartments, sum the results
}
return sum;
}
}
The disadvantage of recursion is that stack overflow may occur; You should carefully consider all if cases otherwise stack overflow happens. Recursive traversals on R’s list.
foo2 <- function(l) {
if ((is.list(l) && !any(sapply(l, is.list))) || is.atomic(l)) {
print(l) # desired manipulation on target children nodes
} else { # recursive case
for (i in seq_along(l)) {
foo2(l[[i]])
}
}
}
A solution in stackoverflow.org
foo <- function(l){
lapply(l, function(x) if(is.list(x) && length(x)==0) "" else if(is.list(x)) foo(x) else x)
}
Recursion on environments from advanced R
where <- function(name, env = caller_env()) {
if (identical(env, empty_env())) {
# Base case
stop("Can't find ", name, call. = FALSE)
} else if (env_has(env, name)) {
# Success case
env
} else {
# Recursive case
where(name, env_parent(env))
}
}
A loop solution on environments traverals
f2 <- function(..., env = caller_env()) {
while (!identical(env, empty_env())) {
if (success) {
# success case
return()
}
# inspect parent
env <- env_parent(env)
}
# base case
}