Controlling Environments in R

r

#1

Hi!

I want to use a recursive function f, which creates the optimal solutions for a problem. Just to be clear, number of optimal solutions is not the number of recursions of the function, but much much less than that. I want to have a counter variable to display the indices of the optimal solutions of the problem.

By far what I have done is that I created a counter globally (using <<-). This works perfectly. But now I have been recommended by our professor to avoid it, if possible.

I came across the R function new.env and it gives me the idea of creating another function g. I will then create an environment under it and define the counter there. Then I will call f and use the counter as I did before.

But I am not really getting how to do it. Any ideas/suggestions/hints?

I know it helps to have a reproducible example, but I can't really provide one.

What I have done is somewhat similar to the following:

# recursive function - function of interest
g <- function(arguments)
{
  if(condition)
  {
    ...
    g(arguments)
  } else
  {
    cat("Solution No.", counter)
    print(solution)
    counter <<- counter + 1
  }
}

# parent function - under which counter will be defined
f <- function(arguments)
{
  counter <<- 1
  g(arguments)
}

And what I want is this:

# parent function - under which counter will be defined
f <- function(arguments)
{
  # create a new environment, say e
  # initialize counter in e as 1
  g(arguments)
}

# recursive function - function of interest
g <- function(arguments)
{
  if(condition)
  {
    ...
    g(arguments)
  } else
  {
    # cat("Solution No.", counter as in e)
    print(solution)
    # update counter by incrementing it by 1
  }
}

Any help will be appreciated.


#2

The main point is that we need a handle for the counter: the counter must be defined in an environment outside g and we need to know how to access that environment. <<- accesses the environment of f in the first example. In the second example, we need to access env e - <<- or assign could do that, thus it is basically the same solution.

In R it is nice to play with closures. This example still uses <<- but the code is compact:

# function defined outside g and f
counter <- function() {
    
    c <- 1
    function() {
        print(c)
        c <<- c + 1
    }
}

tick <- counter()  # creates a new counter

tick()  # we can use tick() wherever we want, even inside g
#> [1] 1
tick()
#> [1] 2

Created on 2018-08-17 by the reprex package (v0.2.0).

Alternatively, if your recursion allows it, g could return counter as part of a list, e.g. list(counter = counter, results = results).


#3

While I liked your solution very much, I can't use your function in my code directly.

I tried two ways.

First, I defined tick <- counter() in the base environment, for the first implementation, it displays correct result. But for the next ones, the counter does not get reset, and it continues to get incremented recursively.

So, I tried to define inside f. But that led to:

Error in tick() : could not find function "tick"

So, I used tick <<- counter(), and it works as I would have liked. But in that case, isn't it the same as what I did earlier?

The problem is the idea of closure is new to me. Can you provide me some reference? Then, possibly I can modify your code to suit my problem.


#4

One reference that should be useful is "Advanced R", specifically chapter on environments. It provides some background on how they work and how they can be used:


#5

First, I defined tick <- counter() in the base environment, for the first implementation, it displays correct result. But for the next ones, the counter does not get reset, and it continues to get incremented recursively.
So, I tried to define inside f . But that led to

I am afraid that your description does not provide enough context for me to understand exactly what you did. A reproducible example will be greatly appreciated (feel free to use Fibonacci numbers to create a simple recursion).


#6

I cannot reproduce the problem I am facing using the Fibonacci sequence. Let me use my own problem.

My assignment is on the pairwise sequence alignment of DNA sequences. I know there are R packages for this, but it's always good to implement on my own to understand the method. Anyway, currently I am implementing Needleman-Wunsch Algorithm.

Here's my first implementation:

Original.pdf (102.0 KB)

Here, I used print_optimal_global_alignment as f and global_alignment as g.

Now, after seeing your solution, I deleted the counter variable and defined your function in the global environment. Now, I can initialise tick (as you did) either in the base environment, or in the g environment. Then, I faced the problems as I said here.

Here are the results I got.

  1. Base Environment.pdf (102.3 KB)
  2. a. f Environment as Local.pdf (100.4 KB)
    b. f Environment as Global.pdf (101.9 KB)

Hope these will help you to understand my problem.

Thanks!


#7

The solution that I suggested should be used as in 1. Base Environment. There tick is defined in the global environment and visible to all functions. If you need to re-init the counter, redefine it before calling global_alignment.

Solution 2.a f Environment as Local does not work because tick is defined in global_alignment and thus not visible in finding_scores.

Solution 2.b f Environment as Global" definestick` in the global environment, thus it is the equivalent of solution 1 with the advantage of redefining the counter.

Please see more about lexical scoping in R: briefly, if R cannot find the definition of a variable in the current
environment, it will look in the environment where the function was defined.

If you want to make (2.) work, my advice is to pass tick as an argument to print_optimal_global_alignment.

(It is OK to copy and paste the reprex code, the PDFs are an obstacle.)


#8

Thanks for the references. I'll be going through these.

And, just for clarification, is there any difference between my original implementation (Example 1) and and defining in f environment globally (Example 2)?


#9

In this particular example, no. Keep in mind that <<- does not go all the way down to the global environment, it stops once it finds the symbol it is looking for (see counter_fn).