Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a specific function run BEFORE every line of code is executed in R?

Tags:

r

Is it possible in R to make a piece of code run before every command?

Just an example of my usecase (not the actual usecase). printing hi before every command

> print(2)
> print(1)

should give

hi
2
hi
1

So, there should be a function say beforeTaskCall(print("hi")) which will call print("hi") before every line.

I found the addTaskCallback which will make a function run after every command.

like image 467
Jay Mundra Avatar asked Oct 15 '25 08:10

Jay Mundra


2 Answers

To run a piece of code before a command you can redefine an unary operator and place this before the command.

`~` <- function(x) {print("hi"); eval(substitute(x))}

~print(2)
#[1] "hi"
#[1] 2

~print(1)
#[1] "hi"
#[1] 1

Other possible unary operators are -, +, ? of !.
Or use binary operator, in case it could be placed after the function

`?` <- function(x, .) {print("hi"); eval(substitute(x))}

print(2) ?.
#[1] "hi"
#[1] 2

Another option will be to store the commands as a string and iterate over them.

s <- c(r"(print(2)
print(1))")

for(x in strsplit(s, "\n")[[1]]) {
  print("hi")
  eval(parse(text=x))
}
#[1] "hi"
#[1] 2
#[1] "hi"
#[1] 1

Or make use of addTaskCallback, which is calling the function afterwards but maybe it could be used in this setup.

invisible(id <- addTaskCallback(function(...) {print("hi"); TRUE}))
#[1] "hi"
print(2)
#[1] 2
#[1] "hi"
invisible(removeTaskCallback(id))
print(1)
#[1] 1
like image 122
GKi Avatar answered Oct 17 '25 02:10

GKi


Approach 1: trace

Use trace to insert arbitrary R expressions (R code) into any places inside a function.

f <- function(x) {
    print(x * 10)
    print(x * 100)
} # Our original function f
m <- expression(print("hello"), print("world")) # An expression m we want to insert into function f
# m <- quote({print("hello"); print("world")}) # Alternative way of defining m
# m <- substitute({print("hello"); print("world")}) # Alternative way of defining m
trace(f, tracer = m, print = FALSE) # Insert expression m at the beginning of function f
f # Check that this function is in trace mode, and read the instruction for seeing tracing code
body(f) # See that our code is inserted correctly
f(3)
[1] "hello"
[1] "world"
[1] 30
[1] 300

untrace(f) # Remove all tracing code
f(3)
[1] 30
[1] 300

With this approach you can insert code at any places inside a function utilizing tracer, exit, at argument of trace, or even interactively edit the function with edit = TRUE argument:
Beginning:

trace(f, tracer = m, print = FALSE)
body(f)
{
    .doTrace(expression(print("hello"), print("world")))
    {
        print(x * 10)
        print(x * 100)
    }
}

End:

trace(f, exit = m, print = FALSE)
body(f)
{
    on.exit(.doTrace(expression(print("hello"), print("world"))))
    {
        print(x * 10)
        print(x * 100)
    }
}

Inside:

trace(f, m, at = 3, print = FALSE)
body(f)
{
    print(x * 10)
    {
        .doTrace(expression(print("hello"), print("world")))
        print(x * 100)
    }
}

Interactive:

trace(f, edit = TRUE, print = FALSE)
# supply your custom version of f using an editor
body(f) # for checking

Approach 2: Build a new function based on body(f)

f <- function(x) {
    print(x * 10)
    print(x * 100)
} # Our original function f
h_f <- f # copy function f as template
body(h_f) <- substitute({print("hello"); print("world"); m}, list(m = body(f))) # change only body(h_f) to suit our needs
# body(h_f) <- bquote({print("hello"); print("world"); .(body(f))}) # Alternative way of defining body(h_f)
h_f # to check the resulting h_f function
#function (x) 
#{
#    print("hello")
#    print("world")
#    {
#        print(x * 10)
#        print(x * 100)
#    }
#}

h_f(3)
[1] "hello"
[1] "world"
[1] 30
[1] 300
like image 34
Hieu Nguyen Avatar answered Oct 17 '25 01:10

Hieu Nguyen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!