2015-05-31 21 views
6

Pisałem kilka makr w R przy użyciu funkcji defmacro z pakietu gtools:Czy używanie makr w R jest bezpieczne?

#' IfLen macro 
#' 
#' Check whether a object has non-zero length, and 
#' eval expression accordingly. 
#' 
#' @param df An object which can be passed to \code{length} 
#' @param body1 If \code{length(df)} is not zero, then this clause is evaluated, otherwise, body2 is evaluated. 
#' @param body2 See above. 
#' @importFrom gtools defmacro 
#' 
#' @examples 
#' ifLen(c(1, 2), { print('yes!') }, {print("no!")}) 
#' 
#' @author kaiyin 
#' @export 
ifLen = gtools::defmacro(df, body1, body2 = {}, expr = { 
      if(length(df) != 0) { 
       body1 
      } else { 
       body2 
      } 
     }) 

#' IfLet macro 
#' 
#' Eval expression x, assign it to a variable, and if that is TRUE, continue 
#' to eval expression1, otherwise eval expression2. Inspired by the clojure 
#' \code{if-let} macro. 
#' 
#' @param sym_str a string that will be converted to a symbol to hold value of \code{x} 
#' @param x the predicate to be evalueated, and to be assigned to a temporary variable as described in \code{sym_str} 
#' @param body1 expression to be evaluated when the temporary variable is TRUE. 
#' @param body2 expression to be evaluated when the temporary variable is FALSE. 
#' @importFrom gtools defmacro 
#' 
#' @examples 
#' ifLet("..temp..", TRUE, {print(paste("true.", as.character(..temp..)))}, 
#'  {print(paste("false.", as.character(..temp..)))}) 
#' 
#' @author kaiyin 
#' @export 
ifLet = gtools::defmacro(sym_str, x, body1, body2={}, expr = { 
      stopifnot(is.character(sym_str)) 
      stopifnot(length(sym_str) == 1) 
      assign(sym_str, x) 
      if(eval(as.symbol(sym_str))) { 
       body1 
      } else { 
       body2 
      } 
     }) 

#' IfLetLen macro 
#' 
#' Similar to ifLet, but conditioned on whether the length of 
#' the result of \code{eval(x)} is 0. 
#' 
#' 
#' @param x the predicate to be evalueated, and to be assigned to a temporary var called \code{..temp..} 
#' @param body1 expression to be evaluated when \code{..temp..} is TRUE. 
#' @param body2 expression to be evaluated when \code{..temp..} is FALSE. 
#' @importFrom gtools defmacro 
#' 
#' @examples 
#' ifLetLen("..temp..", 1:3, {print(paste("true.", as.character(..temp..)))}, 
#'  {print(paste("false.", as.character(..temp..)))}) 
#' 
#' @author kaiyin 
#' @export 
ifLetLen = gtools::defmacro(sym_str, x, body1, body2={}, expr = { 
      stopifnot(is.character(sym_str)) 
      stopifnot(length(sym_str) == 1) 
      assign(sym_str, x) 
      ifLen(eval(as.symbol(sym_str)), { 
       body1 
      }, { 
       body2 
      }) 
     }) 

Czy istnieje niebezpieczeństwo korzystania z nich?

+2

Nawet o nich nie słyszałem. Rozumiem, że generują kod w locie. Dlaczego używałbyś ich zamiast funkcji, szczególnie biorąc pod uwagę potężne zdolności R, aby spojrzeć na swój własny kod? –

+1

Istnieje potencjalne niebezpieczeństwo, że makra nie mają własnego środowiska. Aby uzyskać więcej informacji, zobacz artykuł o nowościach Thomasa Lumleya R, o których mowa na stronie pomocy ([Ostrzeżenie o pliku PDF] (http://cran.r-project.org/doc/Rnews/Rnews_2001-3.pdf)). – Jota

Odpowiedz

5

Oczywiście musisz być ostrożny. Makra nie mają własnego środowiska.

Prosty przykład M (x, y) = (x + y)^2:

m1 <- defmacro(x, y, expr = { 
    x <- x + y 
    x*x 
}) 

Wykonanie m1 zmieni zmiennych wejściowych w środowisku wywołującego:

> x <- 1 
> y <- 2 
> m1(x, y) 
[1] 9 
> x 
[1] 3 
> y 
[1] 2 

normalne funkcje R zachowuj się inaczej:

f1 <- function(x, y) { 
    x <- x + y 
    x*x 
} 

> x <- 1 
> y <- 2 
> f1(x, y) 
[1] 9 
> x 
[1] 1 
> y 
[1] 2 
+0

Ładnie ilustrowane. Czy możesz też na to spojrzeć? http://stackoverflow.com/questions/30562653/defmacro-that-uses-local-variables-in-r – qed

Powiązane problemy