What ways are there to edit a function in R?
Asked Answered
M

5

58

Let's say we have the following function:

foo <- function(x)
{
    line1 <- x
    line2 <- 0
    line3 <- line1 + line2
    return(line3)
}

And that we want to change the second line to be:

    line2 <- 2

How would you do that?

One way is to use

fix(foo)

And change the function.

Another way is to just write the function again.

Is there another way? (Remember, the task was to change just the second line)

What I would like is for some way to represent the function as a vector of strings (well, characters), then change one of it's values, and then turn it into a function again.

Morice answered 16/3, 2010 at 20:47 Comment(4)
And it's not something you can do by passing a parameter to a function?.. Note that you can also pass functions as parameters.Voidable
Hi Leo - the question is for when I want to change a function someone else did, but inside the code to not have to copy paste the entire function.Morice
if you need to edit an "internal" function, I found the following advice and snippets very useful: nabble: how-to-override-replace-a-function-in-a-package-namespaceFoxtrot
Nabble.com no longer exists.Sequestration
G
29

Or take a look at the debugging function trace(). It is probably not exactly what you are looking for but it lets you play around with the changes and it has the nice feature that you can always go back to your original function with untrace(). trace() is part of the base package and comes with a nice and thorough help page.

Start by calling as.list (body(foo)) to see all the lines of your code.

as.list(body(foo))
[[1]]
`{`

[[2]]
line1 <- x

[[3]]
line2 <- 0

[[4]]
line3 <- line1 + line2

[[5]]
return(line3)

Then you simply define what to add to your function and where to place it by defining the arguments in trace().

trace (foo, quote(line2 <- 2), at=4)
foo (2)
[1] 4

I said in the beginning that trace() might not be exactly what you are looking for since you didn't really change your third line of code and instead simply reassigned the value to the object line2 in the following, inserted line of code. It gets clearer if you print out the code of your now traced function

body (foo)
{
    line1 <- x
    line2 <- 0
    {
        .doTrace(line2 <- 2, "step 4")
        line3 <- line1 + line2
    }
    return(line3)
}
Gregor answered 16/3, 2010 at 21:43 Comment(0)
S
46

There is a body<- function that lets you assign new content of the function.

body(foo)[[3]] <- substitute(line2 <- 2)
foo
#-----------    
function (x) 
{
    line1 <- x
    line2 <- 2
    line3 <- line1 + line2
    return(line3)
}

(The "{" is body(foo)[[1]] and each line is a successive element of the list. Therefore the second line is the third element in the expression list. The inserted elements need to be unevaluated expressions rather than text.)

There is also a corresponding formals<- function that lets one perform similar surgery on the argument pairlist.

Note: fixInNamespace is probably a better choice than fix if the function will be calling accessory functional resources in a loaded package. When used from the console, both fix will assign results to the .GlobalEnv.

Sequestration answered 21/3, 2010 at 2:0 Comment(4)
Recently I was forced to do similar thing and one thing can be improved: you can go deeper into the list, so for question case body(foo)[[3]][[3]] <- 2 works and substitute is needless.Malda
Thanks. I thought the substitute was needed to keep the mode of the expression as a language item.Sequestration
If you replace whole line then yes, is needed. Generally: classes should match: my way work cause I replace number by number. If I want to replace, say, line2 to myVariable then as.symbol conversion is needed.Malda
I remembered this approach yesterday when addressing a question about how to build a moment-generating function. It only required a symbolic "append" of *exp(x*t) using substitute, where the argument was delivered as a quote()-ed call object.Sequestration
G
29

Or take a look at the debugging function trace(). It is probably not exactly what you are looking for but it lets you play around with the changes and it has the nice feature that you can always go back to your original function with untrace(). trace() is part of the base package and comes with a nice and thorough help page.

Start by calling as.list (body(foo)) to see all the lines of your code.

as.list(body(foo))
[[1]]
`{`

[[2]]
line1 <- x

[[3]]
line2 <- 0

[[4]]
line3 <- line1 + line2

[[5]]
return(line3)

Then you simply define what to add to your function and where to place it by defining the arguments in trace().

trace (foo, quote(line2 <- 2), at=4)
foo (2)
[1] 4

I said in the beginning that trace() might not be exactly what you are looking for since you didn't really change your third line of code and instead simply reassigned the value to the object line2 in the following, inserted line of code. It gets clearer if you print out the code of your now traced function

body (foo)
{
    line1 <- x
    line2 <- 0
    {
        .doTrace(line2 <- 2, "step 4")
        line3 <- line1 + line2
    }
    return(line3)
}
Gregor answered 16/3, 2010 at 21:43 Comment(0)
A
24

fix is the best way that I know of doing this, although you can also use edit and re-assign it:

foo <- edit(foo)

This is what fix does internally. You might want to do this if you wanted to re-assign your changes to a different name.

Aimee answered 16/3, 2010 at 21:12 Comment(0)
R
17

fixInNamespace is like fix, for functions in a package (including those that haven't been exported).

Ranie answered 7/6, 2010 at 10:19 Comment(1)
I think this might be the best answer. When I am trying to hack functions from packages, I often need to follow the creation of a newly hacked function with environment(new) <- environment(old). This prevent failure to find accessory function that aren't on the search tree accessible from the console-associated environment.Sequestration
G
9

You can use the 'body' function. This function will return the body of function:

fnx = function(a, b) { return(a^2 + 7*a + 9)}
body(fnx)
# returns the body of the function

So a good way to 'edit' a function is to use 'body' on the left-hand side of an assignment statement:

body(fnx) = expression({a^2 + 11*a + 4})
Grandpa answered 16/3, 2010 at 21:40 Comment(1)
That's actually a use of the body<- function.Sequestration

© 2022 - 2024 — McMap. All rights reserved.