在不使用"quote"的情况下推迟表达式求值 [英] Defer expression evaluation without using `quote`

查看:78
本文介绍了在不使用"quote"的情况下推迟表达式求值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了以下函数/示例,作为在表等中显示变量标签的通用方法:

#' Function to prettify the output of another function using a `var.labels` attribute
#' This is particularly useful in combination with read.dta et al.
#' @param dat A data.frame with attr `var.labels` giving descriptions of variables
#' @param expr An expression to evaluate with pretty var.labels
#' @return The result of the expression, with variable names replaced with their labels
#' @examples
#' testDF <- data.frame( a=seq(10),b=runif(10),c=rnorm(10) )
#' attr(testDF,"var.labels") <- c("Identifier","Important Data","Lies, Damn Lies, Statistics")
#' prettify( testDF, quote(str(dat)) )
prettify <- function( dat, expr ) {
  labels <- attr(dat,"var.labels")
  for(i in seq(ncol(dat))) colnames(dat)[i] <- labels[i]
  attr(dat,"var.labels") <- NULL
  eval( expr )
}

我希望用户不必引用传递的表达式.

replicate使用此代码来做到这一点:

eval.parent(substitute(function(...) expr))

但是我不了解它是如何工作的,并且通常在不理解的情况下尝试复制,但是我只是简单地复制此代码或修改代码的尝试都失败了.

如何在不要求用户quote表示其表达式的情况下,编写一个以未计算的表达式作为输入的函数?我认为答案将很大程度上取决于懒惰的评估.

解决方案

用eval和替补回答

在这种情况下,我认为您只需要eval(substitute(expr)). expr是一个承诺,我们可以直接使用expr来获取承诺的值,也可以使用substitute来获取承诺的内容.参见 http://cran.r-project.org/doc/manuals/R-lang.html#Promise-objects 了解详情. Promise的内容为call,因此我们只需eval即可获得新结果.

prettify <- function( dat, expr ) {
  labels <- attr(dat,"var.labels")
  for(i in seq(ncol(dat))) colnames(dat)[i] <- labels[i]
  attr(dat,"var.labels") <- NULL
  eval(substitute(expr))
}

> prettify( testDF, str(dat))
'data.frame':   10 obs. of  3 variables:
 $ Identifier                 : int  1 2 3 4 5 6 7 8 9 10
 $ Important Data             : num  0.336 0.9479 0.1379 0.94 0.0484 ...
 $ Lies, Damn Lies, Statistics: num  1.398 0.654 0.268 -0.397 -0.41 ...

在建议的编辑中,@ user2103369建议replicate是不同的,因为它使用sapply来获取多个求值,因此它需要一个函数而不是一个调用.

默认参数时的不同行为

有趣的是,根据参数是默认参数还是用户添加的参数,promise的行为有所不同.见下文.我认为SoDA可以解决这个问题,但是我没有用.此函数将打印promise的值,并使用eval对其进行评估,然后直接对其进行评估.

foo <- function(a, b=a+1) {
  print(substitute(b))
  print(eval(substitute(b)))
  b
}

直接评估它会在用户提供该值时导致错误.

> foo(a=2, b=a+1)
a + 1
[1] 3
Error in foo(a = 2, b = a + 1) : object 'a' not found

但是默认值有效.

> foo(a=2)
a + 1
[1] 3
[1] 3

在建议的编辑中,@ user2103369说默认参数在函数内部求值,而显式参数在调用框架中求值.因此,在这种情况下,用户提供的值将失败,因为a在调用框架中不可见.

使用函数的替代方法

但是,对我来说(尽管OP表示不同意;我将这一部分留给以后的答案的读者使用),感觉就像这样,更自然地将函数用作第二个参数,就像这样;首先,这意味着用户不必知道函数中的名称为dat.

prettify <- function( dat, FUN ) {
  f <- match.fun(FUN)
  labels <- attr(dat,"var.labels")
  for(i in seq(ncol(dat))) colnames(dat)[i] <- labels[i]
  attr(dat,"var.labels") <- NULL
  f(dat)
}

然后可以使用匿名函数来调用它,我认为这正是您要查找的内容,只是用户也必须键入function(x).

> prettify( testDF, function(x) str(x) )
'data.frame':   10 obs. of  3 variables:
 $ Identifier                 : int  1 2 3 4 5 6 7 8 9 10
 $ Important Data             : num  0.296 0.707 0.883 0.821 0.724 ...
 $ Lies, Damn Lies, Statistics: num  -1.1506 0.4846 -1.824 -0.397 0.0898 ...

或者在简单的情况下(例如您的示例),仅给出函数的名称.

> prettify( testDF, str)
'data.frame':   10 obs. of  3 variables:
 $ Identifier                 : int  1 2 3 4 5 6 7 8 9 10
 $ Important Data             : num  0.296 0.707 0.883 0.821 0.724 ...
 $ Lies, Damn Lies, Statistics: num  -1.1506 0.4846 -1.824 -0.397 0.0898 ...

I have created the following function/example as a generic way to display variable labels in tables and so forth:

#' Function to prettify the output of another function using a `var.labels` attribute
#' This is particularly useful in combination with read.dta et al.
#' @param dat A data.frame with attr `var.labels` giving descriptions of variables
#' @param expr An expression to evaluate with pretty var.labels
#' @return The result of the expression, with variable names replaced with their labels
#' @examples
#' testDF <- data.frame( a=seq(10),b=runif(10),c=rnorm(10) )
#' attr(testDF,"var.labels") <- c("Identifier","Important Data","Lies, Damn Lies, Statistics")
#' prettify( testDF, quote(str(dat)) )
prettify <- function( dat, expr ) {
  labels <- attr(dat,"var.labels")
  for(i in seq(ncol(dat))) colnames(dat)[i] <- labels[i]
  attr(dat,"var.labels") <- NULL
  eval( expr )
}

I'd rather the user not have to quote the expression being passed in, however.

replicate does just that, using this code:

eval.parent(substitute(function(...) expr))

Yet I don't understand how it works, and as is typical for attempts to replicate without comprehension, my attempts to simply copy this code or tinker with it have all failed.

How do I write a function with an unevaluated expression as input, without requiring the user to quote their expression? I assume the answer will rely heavily on lazy evaluation.

解决方案

Answer with eval and substitute

I think to do this in this case you just need eval(substitute(expr)). expr is a promise, and we can either get the value of the promise by using expr directly, or the content of the promise, by using substitute. See http://cran.r-project.org/doc/manuals/R-lang.html#Promise-objects for details. The content of the promise is a call, so we just eval that to get the new result.

prettify <- function( dat, expr ) {
  labels <- attr(dat,"var.labels")
  for(i in seq(ncol(dat))) colnames(dat)[i] <- labels[i]
  attr(dat,"var.labels") <- NULL
  eval(substitute(expr))
}

> prettify( testDF, str(dat))
'data.frame':   10 obs. of  3 variables:
 $ Identifier                 : int  1 2 3 4 5 6 7 8 9 10
 $ Important Data             : num  0.336 0.9479 0.1379 0.94 0.0484 ...
 $ Lies, Damn Lies, Statistics: num  1.398 0.654 0.268 -0.397 -0.41 ...

In a suggested edit, @user2103369 suggests that replicate is different because it uses sapply to get multiple evaluations, so it needs a function rather than a call.

Different behavior when default argument

Interestingly, the promise acts differently depending on if the argument is the default argument or added by the user; see below. I think SoDA addresses this but I don't have it handy. This function prints the value of the promise, evaluates it with eval, and then evaluates it directly.

foo <- function(a, b=a+1) {
  print(substitute(b))
  print(eval(substitute(b)))
  b
}

Evaluating it directly results in an error when the user supplies the value.

> foo(a=2, b=a+1)
a + 1
[1] 3
Error in foo(a = 2, b = a + 1) : object 'a' not found

But the default value works.

> foo(a=2)
a + 1
[1] 3
[1] 3

In a suggested edit, @user2103369 says that the default argument is evaluated inside the function, while an explicit argument is evaluated in the calling frame. So in this case, the user supplied value fails because a is not visible in the calling frame.

An alternate method using a function

However, to me (though the OP disagrees; I'm leaving this part for future readers of this answer), this feels like a case where it's more natural to use a function as the second parameter, like this; for one, this means the user doesn't have to know that it's called dat within the function.

prettify <- function( dat, FUN ) {
  f <- match.fun(FUN)
  labels <- attr(dat,"var.labels")
  for(i in seq(ncol(dat))) colnames(dat)[i] <- labels[i]
  attr(dat,"var.labels") <- NULL
  f(dat)
}

Then it can be called with an anonymous function, which is exactly what you're looking for, I think, except that the user has to type function(x) as well.

> prettify( testDF, function(x) str(x) )
'data.frame':   10 obs. of  3 variables:
 $ Identifier                 : int  1 2 3 4 5 6 7 8 9 10
 $ Important Data             : num  0.296 0.707 0.883 0.821 0.724 ...
 $ Lies, Damn Lies, Statistics: num  -1.1506 0.4846 -1.824 -0.397 0.0898 ...

Or in simple cases, as in your example, with just the name of the function.

> prettify( testDF, str)
'data.frame':   10 obs. of  3 variables:
 $ Identifier                 : int  1 2 3 4 5 6 7 8 9 10
 $ Important Data             : num  0.296 0.707 0.883 0.821 0.724 ...
 $ Lies, Damn Lies, Statistics: num  -1.1506 0.4846 -1.824 -0.397 0.0898 ...

这篇关于在不使用"quote"的情况下推迟表达式求值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆