我需要简单的包装器来从Rcpp代码中序列化任意R对象。下面是我的代码的简化版本:
// [[Rcpp::export]]
Rcpp::RawVector cpp_serialize(RObject x) {
Rcpp::Function serialize = Rcpp::Environment::namespace_env("base")["serialize"];
return serialize(x, R_NilValue);
}
这很好用,但我发现对于类call
的对象,在序列化之前会调用该调用。我怎样才能防止这种情况发生?我只是想在R中模仿serialize()
# Works as intended
identical(serialize(iris, NULL), cpp_serialize(iris))
# Does not work: call is evaluated
call_object <- call("rnorm", 1000)
identical(serialize(call_object, NULL), cpp_serialize(call_object))
更新:我有一个解决方法(见下文),但我仍然对正确的解决方案非常感兴趣。
Rcpp::RawVector cpp_serialize(RObject x) {
Rcpp::Environment env;
env["MY_R_OBJECT"] = x;
Rcpp::ExpressionVector expr("serialize(MY_R_OBJECT, NULL)");
Rcpp::RawVector buf = Rcpp::Rcpp_eval(expr, env);
}
答案 0 :(得分:3)
我认为您在Rcpp::Function
课程中发现了意外行为。 MRE:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
RObject cpp_identity(RObject x) {
Rcpp::Function identity("identity");
return identity(x);
}
/*** R
quoted <- quote(print(1));
identity(quoted)
cpp_identity(quoted)
*/
给出
> quoted <- quote(print(1));
> identity(quoted)
print(1)
> cpp_identity(quoted)
[1] 1
[1] 1
这是因为Rcpp在幕后有效地执行了这项评估:
Rcpp_eval(Rf_lang2(Rf_install("identity"), x))
基本上就像
eval(call("identity", quoted))
但调用对象未受到评估的“保护”。
答案 1 :(得分:1)
tl; dr :问题是如何从C 序列化为Raw向量? RApiSerialization包中的(编译的C)函数serializeToRaw()
提供了R自己的序列化代码。如下面的基准所示,它比上面建议的快三倍。
更长的答案:我不建议使用Rcpp::Function()
来解决这个问题。我们做实际上为R提供了一个适当的程序包来访问序列化:RApiSerialization。它没有做太多,但它只导出两个函数来序列化和反序列化RAW
RcppRedis包需要和使用的Rcpp.package.skeleton()
。
所以我们可以在这里做同样的事情。我刚刚打电话给LinkingTo:
打包了一个包&#39; jeroen&#39;已创建,将Imports:
和imports()
添加到DESCRIPTION,将#include <Rcpp.h>
#include <RApiSerializeAPI.h> // provides C API with serialization
// [[Rcpp::export]]
Rcpp::RawVector cpp_serialize(SEXP s) {
Rcpp::RawVector x = serializeToRaw(s); // from RApiSerialize
return x;
}
添加到NAMESPACE,然后就可以了:
testJeroen <- function() {
## Works as intended
res <- identical(serialize(iris, NULL), cpp_serialize(iris))
## Didn't work above, works now
call_object <- call("rnorm", 1000)
res <- res &&
identical(serialize(call_object, NULL), cpp_serialize(call_object))
res
}
它基本上是你上面的简单版本。
我们可以像你一样打电话给你:
R> library(jeroen)
Loading required package: RApiSerialize
R> testJeroen()
[1] TRUE
R>
并且看,它有效:
Rcpp::Function()
简而言之:如果您不想使用R,请不要使用library(jeroen) # package containing both functions from here
library(microbenchmark)
microbenchmark(cpp=cpp_serialize(iris), # my suggestion
env=env_serialize(iris)) # OP's suggestion, renamed
个对象。
基准测试:使用简单的
edd@max:/tmp/jeroen$ Rscript tests/quick.R
Loading required package: RApiSerialize
Unit: microseconds
expr min lq mean median uq max neval cld
cpp 17.471 22.1225 28.0987 24.4975 26.4795 420.001 100 a
env 85.028 91.0055 94.8772 92.9465 94.9635 236.710 100 b
edd@max:/tmp/jeroen$
我们得到了
{{1}}
显示OP的答案几乎慢了三倍。