0

I have several functions as strings which contain a lot of numeric vectors in the form of c(1,2,3) , with three fixed values each (3D-coordinates). See test_string below as a small example. I can create a working function test_fun using eval and parse, but there is a problem:

I need these vectors to be recognized as one input, i.e. as double[3] and not as language with the parts 'c' (symbol), 1 (double[1]), 2 (double[1]) and 3 (double[1]). Check this code to see what I mean:

test_string <- "function(x) \n c(1,2,3)*x"
test_fun <- eval(parse(text = test_string))
test_fun(2)
#[1] 2 4 6   <- it's working

View(list(test_fun)) # see 'type' column
str(body(test_fun)[[2]])
# language c(1, 2, 3)   <- desired output here: num [1:3] 1 2 3
str(body(test_fun)[[2]][[1]])
# symbol c

Is there an easy solution that works on the full string? I would be very happy to learn about this! If necessary I could also change the code in the function which creates these function strings when the substrings are concatenated with paste("function(x) \n ","c(1,2,3)","*x",sep = "").

Edit: I did a mistake in the 'View' and 'desired output' line. It is now correct.

SophieK
  • 1
  • 3
  • 2
    You could do `eval(body(test_fun)[[2]])`, though if you have all the functions as strings, it might be better (and safer) to use regular expressions to harvest the numeric vectors. – Allan Cameron Jan 27 '22 at 00:03
  • What is this "View(test)" part in your code? `test` is not defined in the code provided. And the output of `View()` is not the best to reproduce an example. – GuedesBF Jan 27 '22 at 01:20
  • Why do you feel that there is a problem? The string has been parsed correctly. Note that `body(function(x) c(1, 2, 3) * x)[[2L]]` gives the same result... – Mikael Jagan Jan 27 '22 at 04:18
  • Thank you very much for your answers! The 'view' part of my code was wrong (I added it last minute and didn't check it, sorry!). – SophieK Jan 27 '22 at 09:13
  • and yes, I can omit the 'list()'. For better explanation: I want to use these functions for typed genetic programming; the function from the package I use must know which parts of the function have which type. That's why I need ```str(body(test_fun)[[2]])``` to already give ```[1] 1 2 3```, I can't access that in another way, e.g. like you have said with ```eval(body(test_fun)[[2]])```, I have to change it before the package function reads it. – SophieK Jan 27 '22 at 09:22
  • It goes in a similar direction as https://stackoverflow.com/questions/2458013/what-ways-are-there-to-edit-a-function-in-r, but I want to change the type of the body part and not the body part itself. – SophieK Jan 27 '22 at 09:29
  • Ok, with ```body(test_fun)[[2]] <- eval(body(test_fun)[[2]])``` I can change this function as desired. So, there is only the question left, how I can find all parts in the body of a nested function that are of type ```symbol 'c'```. Could you help me with that? – SophieK Jan 27 '22 at 11:06

1 Answers1

0

I think I found a solution that works for me. If there is a more elegant solution, please let me know!

I go recursively through the function body and evaluate the parts which are numerical vectors a second time (like @Allan Cameron suggested, thanks!). Here is the function:

evalBodyParts <- function(fun_body){
  for (i in 1:length(fun_body)){ #i=2
    if (typeof(fun_body[[i]])=="language" &&
        typeof(fun_body[[i]][[1]])=="symbol" && fun_body[[i]][[1]]=="c"){
      #if first element is symbol 'c' the whole list is only num [1:3] here
      fun_body[[i]] <- eval(fun_body[[i]])
    } else {
      if(typeof(fun_body[[i]])=="language"){
        fun_body[[i]] <- evalBodyParts(fun_body=fun_body[[i]])
      }
    }
  }
  return(fun_body)
}

To do a quick example which is a bit more complex than the one in the main question above, let me show you the following.

Before:

test_string <- paste("function(x) \n ","c(1,2,3)","*x","+c(7,8,9)",sep = "")
test_fun <- eval(parse(text = test_string))
test_fun(2) # it's working
# [1] 9 12 15
str(body(test_fun)[[2]][[2]])
# language c(1, 2, 3)
str(body(test_fun)[[3]])
# language c(7, 8, 9)

After:

body(test_fun) <- evalBodyParts(fun_body=body(test_fun))
test_fun(2)  # it is still working
# [1] 9 12 15
str(body(test_fun)[[2]][[2]])
# num [1:3] 1 2 3
str(body(test_fun)[[3]])
# num [1:3] 7 8 9
SophieK
  • 1
  • 3