2015-05-31 26 views
6

Tôi đã viết một số macro trong R bằng cách sử dụng chức năng defmacro từ gói gtools:Có an toàn khi sử dụng macro trong R không?

#' 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 
      }) 
     }) 

Có bất kỳ nguy hiểm trong việc sử dụng chúng?

+2

Chưa bao giờ nghe nói về chúng. Tôi lấy nó, họ tạo mã khi đang bay. Tại sao bạn sử dụng chúng thay vì một chức năng, đặc biệt là cho khả năng mạnh mẽ của R để xem mã riêng của nó? –

+1

Có nguy cơ tiềm tàng là các macro không có môi trường riêng của chúng. Xem bài viết R-News của Thomas Lumley được đề cập trên trang trợ giúp ([PDF Warning] (http://cran.r-project.org/doc/Rnews/Rnews_2001-3.pdf)) để biết thêm thông tin. – Jota

Trả lời

5

Tất nhiên bạn phải cẩn thận. Macro không có môi trường riêng của họ.

đơn giản ví dụ f (x, y) = (x + y)^2:

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

Thi m1 sẽ thay đổi các biến đầu vào trong môi trường gọi điện thoại:

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

chức năng R Bình thường hoạt động khác:

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

Minh họa độc đáo. Bạn có thể xem cái này không? http://stackoverflow.com/questions/30562653/defmacro-that-uses-local-variables-in-r – qed

Các vấn đề liên quan