I run the following code
sapply( 0:3, function(x){ls(envir = sys.frame(x))} )
And get the following result
[[1]]
[1] "mat" "mat_inverse"
[[2]]
[1] "FUN" "simplify" "USE.NAMES" "X"
[[3]]
[1] "FUN" "X"
[[4]]
[1] "x"
It seems like it lists all the objects in the current enclosing environment; I do have mat and mat_inverse as two variables. But I am not sure what it returns for [[2]], [[3]], [[4]]. Is there a way to debug this code to track what this code does? Especially the following part:
envir = sys.frame(x)
is very confusing to me.
sys.frame allows you to go back through the calling stack. sys.frame(0) is the beginning of the stack (your initial workspace, so to speak). sys.frame(1) is nested one level deep, sys.frame(2) is nested two levels deep etc.
This code is a good demonstration of what happens when you call sapply. It goes through four environments (numbered 0-3) and prints the objects in each. sapply is in fact a wrapper around lapply. What environments do you get when you actually call this code?
Environment 0 is the beginning, i.e., your entire workspace.
Environment 1 is sapply. Type sapply to see its code. You'll see that the function header has simplify, one of the variables you see in [[2]].
Environment 2 is lapply. Once again, type lapply to see its code; the function header contains FUN and X.
Environment 3 is the function you defined for sapply to run. It only has one variable, x.
As an experiment, run
sapply(0:3, function(x) { howdy = 5; ls(envir = sys.frame(x)) } )
The last line will change to [1] "howdy" "x", because you defined a new variable within that final environment (the function inside lapply inside sapply).
Related
I am trying to understand how first class functions work in R. I had understood that functions were first class in R, but was sorely disappointed when applying that understanding. When a function is saved to a list, whether that be as an ordinary list, a vector or a dictionary style list or vector, it is no longer callable, leading to the following error:
Error: attempt to apply non-function
e.g.
print_func <- function() {
print('hi')
}
print_func()
[1] "hi"
my_list = list(print_func)
my_list[0]()
Error: attempt to apply non-function
my_vector = c(print_func)
my_vector[0]()
Error: attempt to apply non-function
my_map <- c("a" = print_func)
my_map["a"]()
Error: attempt to apply non-function
So why is this? Does R not actually treat functions as first class members in all cases, or is there another reason why this occurs?
I see that R vectors also do unexpected things (for me - perhaps not for experienced R users) to nested arrays:
nested_vector <- c("a" = c("b" = 1))
nested_vector["a"]
<NA>
NA
nested_vector["a.b"]
a.b
1
Here it makes sense to me that "a.b" might reference the sub-member of the key "b" under the key "a". But apparently that logic goes out the window when trying to call the upper level key "a".
R is 1-based; so, you refer to the first element of a vector using index 1 (and not 0 like in python).
There are two approaches to accessing list elements:
accessing list elements while keeping a list (return a list containing the desired elements)
pulling an element out of a list
In the first case, the subsetting is done using a single pair of brackets ([]) and you will always get a list back. Note that this is different from python where you get a list only if you select more than one element (lst = [fun1, fun2]; lst[0] return fun1 and not a one-element list like R while lst[0:2] returns a list).
In the second approach, the subsetting is done using a double pair of brackets ([[]]). you basically pull an element completely out of a list; more like subsetting one element out of a list in python.
print_func <- function() {
print('hi')
}
print_func()
my_list = list(print_func)
mode(my_list[1]) # return a list (not a function); so, it's not callable
[1] "list"
mode(my_list[[1]]) # return a function; so, it's callable
[1] "function"
my_list[1]() # error
my_list[[1]]() # works
[1] "hi"
#
my_vector = c(print_func)
mode(my_vector) # a list, so, not callable
[1] "list"
my_vector[1]() # error because it returns a list and not a function
my_vector[[1]]() # works
[1] "hi"
When subsetting with names, the same logic of single and double pair of brackets applies
my_map <- c("a" = print_func)
mode(my_map) # list, so, not callable
[1] "list"
my_map["a"]() # error
my_map[["a"]]() # works
[1] "hi"
Limey pointed out my 2 issues in the comments. I was using a 0-index and I was using single brackets. If I use a 1-index and double brackets it works, and functions are treated as first class variables.
My issue is resolved, and hopefully I won't make that same mistake again.
I have a function that returns a list.
I would like this list to be immutable, similar to the way that lockBinding prevents overwriting or editing an object.
This would look something like the following:
myfun <- function(x){
out <- list(a = 1, val = x)
make_read_only(out)
out
}
test <- myfun(9)
test$a
[1] 1
test$val
[1] 9
test
$a
[1] 1
$val
[1] 9
test$newval <- 7
Error:
Where make_read_only() is just a standin for a function or some code that accomplishes this task.
I have tried using lockBinding which would work perfectly, however the 'lock' doesn't survive being passed upward into the parent environment after the function returns its output.
I have also looked into trying to lock symbol in the parent environment, but there doesn't seem to be a way to learn what the output will be assigned to from inside the function, which is needed as an argument of lockBinding.
It seems like there might be a way to do this via returning a refernce to an environment and locking the environment using lockEnvironment() or doing something similar, but I would like to hear what options are out there to accomplish this before beginnig.
Similary, it seems like it might be achievable using R6 but I would prefer to avoid using R6 since it is not required for any other part of this codebase and again, I would just like to hear what options are availble to acheive this behaviour.
In summary, the function will return an arbitrary list. This list will be at least a few levels deep.
mylist <- myfun(list(b = list(c = 3), foo = "bar"))
The user should be able to access the sublits/elements in a straightforward way similar to current dollar-sign access
mylist$a$b$c
[1] 3
It should not be possible to edit this object.
mylist$a <- 5
Error:
It should be possible to remove the object via
rm(mylist)
mylist
Error: object 'mylist' not found
So my question is, what available options are there in R to accomplish this?
One way of achieving this could be by creating a new class (locked) along with methods for [<-, [[<-, and $<- for this new class, which will return an error.
For example:
`[[<-.locked` <- function(...) {stop("Can't assign into locked object")}
a<-list(a="a",b=2)
a
$a
[1] "a"
$b
[1] 2
class(a)<-"locked"
a[[1]]<-"moose"
Error in `[[<-.locked`(`*tmp*`, 1, value = "moose") :
Can't assign into locked object
a
$a
[1] "a"
$b
[1] 2
attr(,"class")
[1] "locked"
In that case, all your "make read only" function needs to do is redefine the class for the object as locked:
class(out)<-c("locked",class(out))
I am not sure if it is possible, but I would like to be able to grab the default argument values of a function and test them and the code within my functions without having to remove the commas (this is especially useful in the case when there are many arguments).
In effect, I want to be able to have commas when sending arguments into the function but not have those commas if I copy and paste the arguments and run them by themselves.
For example:
foo=function(
x=1,
y=2,
z=3
) {
bar(x,y,z)
}
Now to test pieces of the function outside of the code block, copy and paste
x=1,
y=2,
z=3
bar(x,y,z)
But this gives an error because there is a comma after x=1
Perhaps I am not asking the right question. If this is strange, what is the preferred method for debugging functions?
Please note, just posted nearly identical question in Julia.
If you want to programmatically get at the arguments of a function and their default values, you can use formals:
fxn = function(a, b, d = 2) {}
formals(fxn)
# $a
#
#
# $b
#
#
# $d
# [1] 2
I suppose if you wanted to store the default value of every argument to your function into a variable of that name (which is what you're asking about in the OP), then you could do this with a for loop:
info <- formals(fxn)
for (varname in names(info)) {
assign(varname, info[[varname]])
}
a
# Error: argument "a" is missing, with no default
b
# Error: argument "b" is missing, with no default
d
# [1] 2
So for arguments without default values, you'll need to provide a value after running this code (as you would expect). For arguments with defaults, they'll now be set.
Many functions' outputs come in the list structure - for example lm(). As a result, you can retrieve the separate section of the output using "$" or indexation with square brackets. My question is how I can create an output in list form, without overtly showing that it is a list. As you know, when a list is printed on the screen, it typically has the sub-list name or index indicated, such as below:
L1 = list(a=1:3, b=letters[1:3])
L1
$a
[1] 1 2 3
$b
[1] "a" "b" "c"
However, lm() output never shows the different sub-lists using "$" and sub-list names - although you can extract these sub-lists using "$" and sub-list names.
A second question pertains to the fact that some functions' outputs include much more than what is actually printed on the screen (e.g., lm(), again). If we use str() for the fitted model, we will see a whole bunch of content within the fitted model most of which is not printed. How is this achieved? Does the function print something and output something else (using invisible()) separately like below?
foo = function(x){
result = list(data=x, test.result=t.test(x))
print(result[[2]])
invisible(result)
}
foo(1:10)$data
Thanks!
Read up on S3 classes. Specifically, when you type just an object name at the prompt, R calls print, which has methods for different classes.
Take a look at print.lm to see how this works.
> test <- list()
> class(test) <- "myclass"
> print.myclass <- function(x) cat("hi")
> test
hi
This question already has answers here:
How to assign from a function which returns more than one value?
(16 answers)
Closed 10 years ago.
Is there a way to output e.g. 2 objects without using list()?
my.fun=function(vector, index)
{
a=fun.a(vector, index)
b=fun.b(vector, index)
output=list(a,b)
}
Or to output 2 lists of objects? Considering that I could also have:
c=fun.a(vector, index)
d=fun.b(vector, index)
And wanted list(a,b) and another list(c,d) for the same function.
This is just a small example for what I am looking for, my function is applied to large objects and I export them as a list, however I would like to export also an intermidiate calculation. One option would be to merge all in the same list, but I would like to know if there is another solution to this.
You can only return one object in a function. But you have some other options. You could assign intermediate objects to the global environment (you need to be careful not to overwrite anything) or you could pass an environment to your function and assign objects to it.
Here's an example of the latter suggestion:
fun <- function(x, env) {
env$x2 <- x^2
x^3
}
set.seed(21)
x <- rnorm(10)
myEnv <- new.env()
fun(x, myEnv)
# [1] 4.987021e-01 1.424421e-01 5.324742e+00 -2.054855e+00 1.061014e+01
# [6] 8.125632e-02 -3.871369e+00 -8.171530e-01 2.559674e-04 -1.370917e-08
myEnv$x2
# [1] 6.288699e-01 2.727464e-01 3.049292e+00 1.616296e+00 4.828521e+00
# [6] 1.876023e-01 2.465527e+00 8.740486e-01 4.031405e-03 5.728058e-06
I found list2env ideal for what you're describing; the trickiest bit, for me, was working out what to give for the env parameter:
f=function(){
list(a=1,b="my string")
}
ret=f()
list2env(ret,env=environment())
#a=ret$a;b=ret$b #Same as previous line
print(a);print(b) #1 and "my string"
The return() object needs to be one thing ... a list or a vector. Since a list can be that "one thing" and a list can hold many things of many classes, all you need to di is initialize a list structure and then push things into it until you are ready to retrun that structure to the calling anvironment.
If you want to "push" individual items into the global (or other environment), you can use <<- or assign, although that is considered ugly practice and a violation of the paradigm of functional programming.
I believe you are limited to using lists, but you can combine them like:
> list(list(a=1, b=2), list(c=3, d=4))
[[1]]
[[1]]$a
[1] 1
[[1]]$b
[1] 2
[[2]]
[[2]]$c
[1] 3
[[2]]$d
[1] 4