gpt4 book ai didi

r - 为什么惰性求值 "lose"是函数参数?

转载 作者:行者123 更新时间:2023-12-02 13:48:32 25 4
gpt4 key购买 nike

我正在处理 SQL 和用户输入。因此,我使用 glue 库来处理参数化查询。

但是,为了保持简洁,我将所有内容包装在一个函数中:

safeQuery <- function(con, sql, ...) {  
sql = glue_sql(sql, ..., .con=con)
query <- dbSendQuery(con, sql)
out <- dbFetch(query)
dbClearResult(query)
return(out)
}

因此,我只需使用连接、SQL 代码以及 glue_sql 适当绑定(bind)的该 SQL 代码的参数列表来调用该函数。

这工作得很好。

现在,我有一个特定的 SQL 调用,我经常以这样或那样的方式使用它,但参数各不相同。

所以我决定为此创建一个函数:

get_data <- function(con, params) {
safeQuery(con,
"SELECT *
FROM foo
WHERE bar IN ({vars*})",
vars=params)
}
p = c(1, 2)
get_data(con, p)

因此,用户数据(在本例中为 c(1, 2))将被传递到 get_data,后者会将其与 SQL 调用一起传递给 safeQuery,其中 glue_sql 将负责绑定(bind)。

但是,如果我实际尝试运行 get_data,则会收到错误

object 'params' not found

谷歌搜索和 SO'ing 已经清楚地表明这与 R 的惰性求值有关。

事实上,将 get_data 更改为

get_data <- function(con, params) {
do.call("safeQuery",
list(con,
"SELECT *
FROM foo
WHERE bar IN ({vars*})",
vars=params)
}

(按照 this answer 的建议)工作得很好,因为 do.call 在将列表中的参数发送到 safeQuery 之前先评估它们。

我不明白的是为什么这是必要的。毕竟,params 的值在传送到 glue_sql 的过程中的任何步骤都不会被修改,因此它应该仍然可用。

链接的答案讨论了使用substitute(我还阅读了关于该主题的this R-bloggers post)将参数的名称替换为调用者的名称(或者如果参数值则替换为它的实际值)是直接给出的),但这对我来说不起作用。修改 get_data 以使用 substitute:

get_data <- function(con, params) {
do.call("safeQuery",
list(con,
"SELECT *
FROM foo
WHERE bar IN ({vars*})",
vars=substitute(params))
}

glue_sql 生成以下 SQL:

SELECT *
FROM foo
WHERE bar IN (params)

而不是params的实际值。我无法在 safeQuery 中尝试相同的操作,因为参数隐藏在 ... 中,并且 substitute(...) 不起作用。我试过了。

我还尝试在 get_data 开始时调用 force(params),但这给出了相同的未找到对象 错误。

get_data <- function(con, params) {
force(params)
do.call("safeQuery",
list(con,
"SELECT *
FROM foo
WHERE bar IN ({vars*})",
vars=params)
}

那么,为什么标准调用时 params 会“丢失”呢?为什么do.call可以工作,但force(params)不行?是否可以使用标准评估来完成这项工作?

我不会撒谎:这段经历让我对如何编写函数和处理它们的参数感到困惑(我考虑从现在开始只使用 do.call)。如果可以在不过度扩展这个问题范围的情况下给出提示,我将非常感激。

最佳答案

我不太清楚为什么会这样,但确实如此。

safeQuery <- function(con, sql, ...) {
dots = list(...)
dots
}

然后当你调用get_data("foo_con", params = 1:3)时,你会得到:

$`vars`
[1] 1 2 3

现在我们在命名列表中有参数,这意味着您应该使用 glue_data (或 glue_data_sql):

safeQuery <- function(con, sql, ...) {
dots = list(...)
glue_data_sql(.x = dots, sql, .con=con)
# More code...
}

现在,当您调用 get_data("foo_con", params = 1:3) 时,您会得到:

<SQL> SELECT *
FROM foo
WHERE bar IN (1, 2, 3)
<小时/>

替代版本:

问题在于参数在哪个环境中求值。强制执行此操作的一种方法是传递您想要的环境:

safeQuery <- function(con, sql, ..., .envir = parent.frame()) {
dots = list(...)
glue_sql(sql, ..., .con=con, .envir = .envir)
# More code...
}

get_data <- function(con, params) {

env <- environment()

safeQuery(con,
"SELECT *
FROM foo
WHERE bar IN ({vars*})",
vars=params, .envir = env)
}

get_data("foo_con", params = 1:3)
<SQL> SELECT *
FROM foo
WHERE bar IN (1, 2, 3)

关于r - 为什么惰性求值 "lose"是函数参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57299608/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com