通过函数传递表达式 [英] Passing expression through functions

查看:109
本文介绍了通过函数传递表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 data.table 包并尝试编写一个函数(如下所示):

  require(data.table)
#函数定义
f = function(path,key){
table = data.table(read.delim setkeyv(x,cols,verbose = verbose)中的错误:一些列不在数据表中(表格, :e
return(table)
}

#用法
f(table.csv,ID)
/ pre>

这里我尝试传递一个表达式到函数。为什么这个代码不工作?



我已经尝试过 substitute() quote / code>和 eval()

解决方案

首先,让我们来看看如何 setkey 函数从 data.table 包执行操作:



 #setkey function 
function(x,...,verbose = getOption(datatable.verbose))
{
if .character(x))
stop(x可能不再是data.table的字符名,可能是未记录的,已被删除。)
cols = getdots()
if(!length(cols))
cols = colnames(x)
else if(identical(cols,NULL))
cols = NULL
setkeyv(x,因此,当你这样做:

  require(data.table)
dt< - data.table(ID = c(1,1,2,2, 3),y = 1:5)
setkey(dt,ID)

data.table (即不导出)内部的函数 getdots 。让我们看一下这个函数:

 #data.table ::: getdots 
function()
{
as.character(match.call(sys.function(-1),call = sys.call(-1),
expand.dots = FALSE)$ ...) b}

那么,它接受在 setkey 中输入的参数,并使用 match.call 分别提取参数。也就是说,此示例案例的 match.call 参数为:

  setkey(x = dt,... = list(ID))

列表中,您可以使用 $ ... 访问 ... 参数,值 ID ,并将此列表转换为包含 as.character 的字符,结果为 (字符向量)。然后 setkey 在内部将此传递给 setkeyv 以设置键。






现在为什么在函数中写 setkey(table,key) h3>

这正是因为 setkey / getdots 的方式。 setkey 函数设计为在第一个参数(即 data.table )之后接受任何参数,然后返回 ... 参数作为字符。



也就是说,如果您给予 setkey(dt,key),则会返回 cols < - key。如果你给 setkey(dt,e),它会返回 cols< - e。它不查找key是否为现有变量,如果是,则替换变量的值。它所做的一切就是将你提供的值(不管是符号还是字符)转换回一个字符。



当然这不会在你的情况下工作,因为你要在 setkey 中提供 = ID中的值。至少我不能想到一个办法做到这一点。






如何解决这个问题?



如@agstudy已经提到,最好/最简单的方法是传递ID并使用 setkeyv 。但是,如果你真的坚持使用 f(table.csv,ID),那么,你可以这样做:

  f < -  function(path,key){
table = data.table(read.delim(path,header = TRUE))
e = as.character(match.call(f)$ key)
setkeyv(table,e)
return(table)
}
pre>

这里,您首先使用 match.call 获取对应于参数,然后将其转换为字符,然后将其传递给 setkeyv p>

总之, setkey 在内部使用 setkeyv 。和imho,setkey是一个方便的函数,当你已经知道要为其设置键的 data.table 的列名时使用。希望这有帮助。


I'm using data.table package and trying to write a function (shown below):

require(data.table)
# Function definition
f = function(path, key) {
  table = data.table(read.delim(path, header=TRUE))
  e = substitute(key)
  setkey(table, e) # <- Error in setkeyv(x, cols, verbose = verbose) : some columns are not in the data.table: e
  return(table)
}

# Usage
f("table.csv", ID)

Here I try to pass an expression to the function. Why this code doesn't work?

I've already tried different combinations of substitute(), quote() and eval(). So, it'd be great if you could also explain how to get this to work.

解决方案

First, let's look at how the setkey function does things from the data.table package:

# setkey function
function (x, ..., verbose = getOption("datatable.verbose")) 
{
    if (is.character(x)) 
        stop("x may no longer be the character name of the data.table. The possibility was undocumented and has been removed.")
    cols = getdots()
    if (!length(cols)) 
        cols = colnames(x)
    else if (identical(cols, "NULL")) 
        cols = NULL
    setkeyv(x, cols, verbose = verbose)
}

So, when you do:

require(data.table)
dt <- data.table(ID=c(1,1,2,2,3), y = 1:5)
setkey(dt, ID)

It calls the function getdots which is internal to data.table (that is, it's not exported). Let's have a look at that function:

# data.table:::getdots
function () 
{
    as.character(match.call(sys.function(-1), call = sys.call(-1), 
        expand.dots = FALSE)$...)
}

So, what does this do? It takes the parameter you entered in setkey and it uses match.call to extract the arguments separately. That is, the match.call argument for this example case would be:

setkey(x = dt, ... = list(ID))

and since it's a list, you can access the ... parameter with $... to get a list of 1 element with its value ID and converting to this list to a character with as.character results in "ID" (a character vector). And then setkey passes this to setkeyv internally to set the keys.


Now why doesn't this work when you write setkey(table, key) inside your function?

This is precisely because of the way setkey/getdots is. The setkey function is designed to take any argument after the first argument (which is a data.table) and then return the ... argument as a character.

That is, if you give setkey(dt, key) then it'll return cols <- "key". If you give setkey(dt, e), it'll give back cols <- "e". It doesn't look for if "key" is an existing variable and then if so substitute the value of the variable. All it does is convert the value you provide (whether it be a symbol or character) back to a character.

Of course this won't work in your case because you want the value in key = ID to be provided in setkey. At least I can't think of a way to do this.


How to get around this?

As @agstudy already mentions, the best/easiest way is to pass "ID" and use setkeyv. But, if you really insist on using f("table.csv", ID) then, this is what you could do:

f <- function(path, key) {
    table = data.table(read.delim(path, header=TRUE))
    e = as.character(match.call(f)$key)
    setkeyv(table, e)
    return(table)
}

Here, you first use match.call to get the value corresponding to argument key and then convert it to a character and then pass that to setkeyv.

In short, setkey internally uses setkeyv. And imho, setkey is a convenient function to be used when you already know the column name of the data.table for which you need to set the key. Hope this helps.

这篇关于通过函数传递表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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