higher level functions in R - is there an official compose operator or curry function?

后端 未结 4 1189
春和景丽
春和景丽 2020-12-07 11:05

I can create a compose operator in R:

 `%c%` = function(x,y)function(...)x(y(...)) 

To be used like this:

 > numericNull         


        
4条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2020-12-07 11:46

    A more complex approach is required if you want the 'names' of the variables to pass through accurately.

    For example, if you do plot(rnorm(1000),rnorm(1000)) then you will get nice labels on your x- and y- axes. Another example of this is data.frame

    > data.frame( rnorm(5), rnorm(5), first=rpois(5,1), second=rbinom(5,1,0.5) )
        rnorm.5. rnorm.5..1 first second
    1  0.1964190 -0.2949770     0      0
    2  0.4750665  0.8849750     1      0
    3 -0.7829424  0.4174636     2      0
    4  1.6551403  1.3547863     0      1
    5  1.4044107 -0.4216046     0      0
    

    Not that the data.frame has assigned useful names to the columns.

    Some implementations of Curry may not do this properly, leading to unreadable column names and plot labels. Instead, I now use something like this:

    Curry <- function(FUN, ...) {
        .orig = match.call()
        .orig[[1]] <- NULL # Remove first item, which matches Curry
        .orig[[1]] <- NULL # Remove another item, which matches FUN
        function(...) {
            .inner = match.call()
            .inner[[1]] <- NULL # Remove first item, which matches Curry
            do.call(FUN, c(.orig, .inner), envir=parent.frame())
        }
    }
    

    This is quite complex, but I think it's correct. match.call will catch all args, fully remembering what expressions defined the args (this is necessary for nice labels). The problem is that it catches too many args -- not just the ... but also the FUN. It also remembers the name of the function that's being called (Curry).

    Therefore, we want to delete these first two entries in .orig so that .orig really just corresponds to the ... arguments. That's why we do .orig[[1]]<-NULL twice - each time deletes an entry and shifts everything else to the left.

    This completes the definition and we can now do the following to get exactly the same as above

    Curry(data.frame, rnorm(5), rnorm(5) )( first=rpois(5,1) , second=rbinom(5,1,0.5) )
    

    A final note on envir=parent.frame(). I used this to ensure that there won't be a problem if you have external variables called '.inner' or '.orig'. Now, all variables are evaluated in the place where the curry is called.

提交回复
热议问题