1

I need to write a function to add a particular command/string of characters to an existing function.

Consider this function:

constraint = function(cov_matrix,max_variance){ 

function(x){
f = NULL 
g = NULL 
g = rbind(g,  t(x)%*%cov_matrix%*%x-max_variance) 
return(list(ceq = f, c= g)) 
} 
}

Ideally, I would like to write a function add_text() that would take as input ("some_text_or_command", constraint) that would generate the output.

constraint = function(cov_matrix, max_variance){ 

function(x){
f = NULL 
g = NULL 
g = rbind(g,  t(x)%*%cov_matrix%*%x-  max_variance) 
"some_text_or_command"
return(list(ceq = f, c= g)) 
} 

}

How to go about it?

8
  • 3
    Your (nested) function is a bit confusing to me, so I am not quite sure what you need. But these functions are going to be your friends to achieve your goal: (1) body() (2) formals() (3) parse() (4) as.call() (5) deparse() (6) eval(). You probably need to do something with gsub() or grep() to inject your "command" on the desired line. All in all, this seems like a very strange thing, I suspect XY problem. Commented Jun 9 at 16:33
  • Since the question is tagged eval, run and read fortunes::fortune(106). Commented Jun 9 at 17:01
  • The point of using this functional (nested function) is to avoid using body(), which very tricky here. As such, deparsing, adding a line at the right position and reparsing may do the trick here. How to do it well? Commented Jun 9 at 17:27
  • Don't deparse and reparse. Use body(), and edit the parsed version of the function. But your question is incomplete: How is add_text() supposed to figure out where to add the new code? Commented Jun 9 at 18:12
  • 1
    I strongly suspect that this is an XY problem. If OP were to explain their ultimate objective, rather than how they are trying to achieve it, we might well be able to suggest a more effective solution. Commented Jun 9 at 19:12

1 Answer 1

1

Here's how to do it, except for one crucial step:

add_text <- function(addition, original) {
  body <- body(original)

  # the missing step:  figure out the path to the line before where you're putting
  # the addition, i.e. the line 
  #   g = rbind(g,  t(x)%*%cov_matrix%*%x-  max_variance) ` 
  # in your example.  The path in your example is `c(2,3,4)`, so
  # I'll use that.

  prev_line <- c(2,3,4)

  len <- length(prev_line)   # How deeply nested is that line?

  # Now the edits:

  if (len > 1)   # Is that line nested?  It usually is, but not always
    parent <- body[[prev_line[-len]]]
  else
    parent <- body

  insert_at <- prev_line[len]
  
  parent <- as.list(parent)
  parent <- append(parent, list(substitute(addition)), after = insert_at)
  parent <- as.call(parent)
  if (len > 1)
    body[[prev_line[-len]]] <- parent
  else 
    body <- parent

  body(original) <- body
  original
}

For example,

add_text(1 + 1, constraint)

will give the function

function (cov_matrix, max_variance) 
{
    function(x) {
        f = NULL
        g = NULL
        g = rbind(g, t(x) %*% cov_matrix %*% x - max_variance)
        1 + 1
        return(list(ceq = f, c = g))
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Brilliant. From a practical standpoint, it works well. In particular, in iterated calls it provides the right function. To answer your concern, one may add the "constraint" that the addition is to be placed right before return(list(ceq = f, c = g)). In iterated calls, your function does not behave this way. Mild issue though, for a highly impressive answer.
: I'd suggest adding a recursive search through the body for a call to "return", and do the insertion just before that. You need to recurse into calls to { and function and some others, e.g. for, while, etc. It'd be too big to post here.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.