开发者

Can I remove an element in ... (dot-dot-dot) and pass it on?

开发者 https://www.devze.com 2023-03-27 16:38 出处:网络
Is it possible to remove an element from ... and pass ... onto other functions?My first two attempts failed:

Is it possible to remove an element from ... and pass ... onto other functions? My first two attempts failed:

parent = function(...)
{

  开发者_开发技巧 a = list(...)
   str(a)
   a$toRemove = NULL  
   str(a)

   # attempt 1   
   child(a)   

   # attempt 2
   child( ... = a )
}


child = function(...)
{
  a = list( ... )
  str(a)
}

parent( a = 1 , toRemove = 2 )

Edit

Sorry about the confusion. I fixed child(). The intent was to have child list the contents of ...

Edit2

Here's more of a real-world example (but still fairly simple so we can have a useful conversation about it). Parent is called via recursion. Parent need to know the depth of the recursive call. Callers outside of parent should't know about "depth" nor should they set it when calling parent(). Parent calls other functions, in this case child(). Child needs values in ... Clearly child doesn't need "depth" because parent generated it for its own use.

parent = function( ... )
{

   depth = list(...)$depth      
   if ( is.null( depth ) )
   {
       depth = 1
   }  
   print( depth )

   # parent needs value of depth to perform various calculations (not shown here)

   if ( depth == 5 )
   {
       return()
   }
   else
   {
      # child doesn't need "depth" in ...
      child( ... ) 
   }

   # yikes!  now we've added a second, third, etc. depth value to ...
   parent( depth = depth + 1 , ... )

}


child = function(...) 
{       
    # does some magic    
}


One way to manipulate these things is to wrap the child function inside parent, and use a definition that puts any arguments you don't want passing on to child after the ... argument. For example:

parent <- function(...) {
    localChild <- function(..., toRemove) child(...)
    localChild(...)
}
child <- function(a) {
    a + 10
}

> parent(a = 1, toRemove = 10)
[1] 11

Another way is to use do.call():

parent2 <- function(...) {
    a <- list(...)
    a$toRemove <- NULL
    do.call(child2, a)
}
child2 <- function(b) {
    b + 10
}
> parent2(b = 1, toRemove = 10)
[1] 11

Depending on your actual use case, the do.call() is perhaps closest to what you intended with your Question.


Your child function is erroneous. Try

> child(a=1)
Error in str(a) : object 'a' not found

edit : no longer applicable.

The ... argument should only be used to pass parameters to a next function. You cannot get the parameters from there that easily, unless you convert them to a list. So your child function could be :

child <- function(...)
{
  mc <- match.call()  # or mc <- list(...)
  str(mc$a)
}

Which doesn't make sense. You can't know whether the user specified a or not. The correct way would be to include a as an argument in your function. the ... is to pass arguments to the next one :

child <- function(a, ...){
    str(a,...)
}

Then you could do :

parent <- function(...){

   mc <- match.call()
   mc$toRemove <- NULL
   mc[[1L]] <- as.name("child")
   eval(mc)

}

or use the list(...) and do.call() construct @Gavin proposed. The benefit of match.call() is that you can include non-dot arguments as well. This allows your parent function to specify defaults for the child :

parent <- function(a=3, ...){
    ... (see above)
}


Here's an example of how to get the items out of ... and remove an element and then I call the next function with do.call:

parent <- function(...){
   funArgs <-  list(...)
   str(funArgs)
   ## remove the second item
   newArgs <- funArgs[-2]
   str(newArgs)
   ## if you want to call another function, use do.call
   do.call(child, newArgs)
  }

child = function(...)
{
  cat("Don't call me a child, buddy!\n")
  a <- list(...)
  str(a)
}


parent(a=1, b=2, c=3)

If you need to add more items to your arguments, as opposed to removing arguments, keep in mind that do.call likes named lists where the names are the argument names and the list values are the argument values. It's in the help file, but I struggled with that a bit before finally figuring it out.


You're getting some good answers, but here's something simple that addresses your specific example:

parent = function(...)
{

   a = list(...)
   str(a)
   a$toRemove = NULL  
   str(a)

   # attempt 1   
   child(a)   

   # attempt 2
   #child(...)
}

child = function(...)
{
    a <- as.list(...)   
    str(a)
}

parent( a = 1 , toRemove = 2 )

which returns:

List of 2
 $ a       : num 1
 $ toRemove: num 2
List of 1
 $ a: num 1
List of 1
 $ a: num 1

Your original version was throwing an error, since a wasn't defined in child. Then simply using as.list(...) in child (as opposed to just list(...)) seems to generate the output you want. Note that I'm using your attempt 1 only here.


I don't think the listed answers solve the problem, or at least not as I read it. Suppose you wanted to pass some parameters, like say 'xmax' and 'xmin' , to child(...) as actual variables?
in child's environment, it wants to see variables named 'xmax' and 'xmin', and the examples presented so far do not seem to make those variables available. Try inserting a line like

xmax-xmin -> xrange

into the child() function and it'll throw an error.
The whole point of the original question, I thought, was to allow passing a subset of the optional "..." variables to child() . You can tailor the solutions for simple cases, e.g. sum(...), where sum(unlist(the_modified_list)) works. In the more general case, I still can't see a solution. We probably need to elevate this problem to the R-help mailing list.

Edit: see the detailed presentation at http://ucfagls.wordpress.com/2011/07/23/passing-non-graphical-parameters-to-graphical-functions-using/

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号