1

I'm wondering is there any way to feed more than 2 argument to an operator, or I dont have any choice except using function format! For example I could define merge operator which needs more than 2 Arg.

Here is just an example, I'm not interested in merge functionality:

1.I made an operator to merge two data frames with their common column names A%>>%B:

`%>>%`<- function(a,b){
      by.tmp = intersect(names(a),names(b))
      base::merge(a,b,by = by.tmp)
}

2.Now I'm looking for a way to define which column should be matched from data.frame A to data.frame B, like:

A %column_name_a>>column_name_b% B
# or feeding 2 arg from each side to operator like follow 
# which inside parenthesis return another operator
A (column_name_a %>>% column_name_b) B
# or more advanced
A:column_name_list_a%>>%column_name_list_b:B

I know how to do it with functions, I just want to know is there a way to define more complicated operators to abstract my code.

UPDATE: I managed to right an operator with unknown number of arguments (it is kinda of cascading but it works). here is the approach:

`%>%` <- function(a,b){

  ifvalid <- function(a, frame = parent.frame()){
    res = try(eval(a,frame),silent = T)
    flag = inherits(res, "try-error") | (length(res)==0)
    ifelse ((!flag) | (length(a)==1) , return(res),  return(
      lapply(a, ifvalid,frame=frame)
      )
    )
  }


  left_arg = substitute(a)
  right_arg= substitute(b)

  res = list(
    left = ifvalid(left_arg),
    right = ifvalid(right_arg)
  )

  return(res)
}

Example run is:

"X":1:NULL %>% date():`*`
# $left
# $left[[1]]
# .Primitive(":")
# 
# $left[[2]]
# $left[[2]][[1]]
# .Primitive(":")
# 
# $left[[2]][[2]]
# [1] "X"
# 
# $left[[2]][[3]]
# [1] 1
# 
# 
# $left[[3]]
# list()
# 
# 
# $right
# $right[[1]]
# .Primitive(":")
# 
# $right[[2]]
# [1] "Wed Feb 03 16:04:17 2016"
# 
# $right[[3]]
# function (e1, e2)  .Primitive("*")
Mahdi Jadaliha
  • 1,947
  • 1
  • 14
  • 22
  • Have the second item passed to your operator be a list with the dataframe as the first element as the column names as the second element. – IRTFM Feb 02 '16 at 21:09
  • 1
    [Binary operators](https://cran.r-project.org/doc/manuals/r-release/R-intro.html#Defining-new-binary-operators) cannot have more than two arguments, by definition. R does not come with a ternary operator, but [others have created them](http://stackoverflow.com/questions/8790143/does-the-ternary-operator-exist-in-r). – Joshua Ulrich Feb 02 '16 at 21:10
  • Thanks Joshua Ulrich. your comment helped a lot. In post you mentioned there was an answer that migh help to make operator with any number of argument: http://stackoverflow.com/a/8792474/3019570 – Mahdi Jadaliha Feb 03 '16 at 20:55

1 Answers1

2

Illustrating the approach outlined above in a comment:

 `%>>%`<- function(a,b){
             base::merge(a,b[[1]],by = b[[2]])
 }

 authors %>>% list(books, "name")
 #-------
      name nationality deceased                         title other.author
1   McNeil   Australia       no     Interactive Data Analysis         <NA>
2   Ripley          UK       no            Spatial Statistics         <NA>
3   Ripley          UK       no         Stochastic Simulation         <NA>
4  Tierney          US       no                     LISP-STAT         <NA>
5    Tukey          US      yes     Exploratory Data Analysis         <NA>
6 Venables   Australia       no Modern Applied Statistics ...       Ripley

Date used is a slightly simplified version of the code on the ?merge help page:

authors <- data.frame(
    name = I(c("Tukey", "Venables", "Tierney", "Ripley", "McNeil")),
    nationality = c("US", "Australia", "US", "UK", "Australia"),
    deceased = c("yes", rep("no", 4)))
books <- data.frame(
    name = I(c("Tukey", "Venables", "Tierney",
             "Ripley", "Ripley", "McNeil", "R Core")),
    title = c("Exploratory Data Analysis",
              "Modern Applied Statistics ...",
              "LISP-STAT",
              "Spatial Statistics", "Stochastic Simulation",
              "Interactive Data Analysis",
              "An Introduction to R"),
    other.author = c(NA, "Ripley", NA, NA, NA, NA,
                     "Venables & Smith"))
IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • Seems like risky business though. – Rich Scriven Feb 02 '16 at 21:18
  • Admittedly. Eventually we might need to include provisions that would make this look like the merge-function itself. I do think this is basically what Hadley does in dplyr. Could also try designing it to take 2 lists with optional arguments. If there were no arguments it could default to using the intersection, which is what merge does anyway, right? – IRTFM Feb 02 '16 at 21:20
  • thanks 42-, this merge was an example. Generally speaking I'm just interested to pass more than 2 arg to an operator. Is there anyway except combineing all [Arg2 - Argn]? Maybe cascading operator like ggplot is a choice, but is there any other way to abstract code even more? – Mahdi Jadaliha Feb 03 '16 at 19:33
  • R has the three dots facility for that task. – IRTFM Feb 03 '16 at 20:08
  • so I made this operator with `...`: ``%^%` <- function(...){return(list(...))}` it is easy to run this with 2 Arg: `1 %^% 2`, now how I shall run it with more than 2 Arg, if I don't use function syntax like `%^%`(1,2,3) – Mahdi Jadaliha Feb 03 '16 at 20:39
  • I don't understand the goal of this effort yet. The user-defined functions really only take 2 arguments but the first or second argument can be a list. (As is demonstrated above.) The definition in your comment only has one argument and when try to use it you would find that the second argument was ignored. I would have thought it would be something like `'%^%' <- function(x, ...){ args <- list(...); func(x, args)}`perhaps with `match.args` to properly qualify the arguments from the function alist. And I really advise against using `%^%` since most people with think it's going to be matrix-op. – IRTFM Feb 03 '16 at 22:41