bre*_*auv 6 r rust rextendr extendr
rextendr我正在尝试使用R 包和 Rust箱来学习如何连接 Rust 和 R(除了学习 Rust 本身)extendr。当我在 R 函数的输入中Expected a vector type.使用时遇到错误。:
下面是一个简单函数的示例,TRUE如果输入向量中的所有值都为正,则返回,FALSE否则返回。当我在输入中使用时它似乎工作正常c(),但当我使用:.
library(rextendr)\n\n# create a Rust function that checks if all values in the input are\n# greater than 0\nrust_function(\n "fn all_positive(input: &[f64]) -> bool {\n let mut out = true;\n for i in input.iter() {\n if *i <= 0.0 {\n out = false;\n break\n } \n }\n out\n }"\n)\n#> \xe2\x84\xb9 build directory: \'/tmp/RtmpeoMSEH/file7f971642e99e\'\n#> \xe2\x9c\x94 Writing \'/tmp/RtmpeoMSEH/file7f971642e99e/target/extendr_wrappers.R\'.\n\n# call it from R\nall_positive(c(1, 2, 3))\n#> [1] TRUE\n\nall_positive(c(0, 1, 2))\n#> [1] FALSE\n\nall_positive(1:3)\n#> Error in all_positive(1:3): Expected a vector type.\nRun Code Online (Sandbox Code Playgroud)\n这是为什么?我该如何解决它?
\nPS:由于我是从 Rust 开始的,请随意提及 Rust 代码中的任何其他错误/非惯用的内容。
\ninteger和numeric类型的Rust 函数1:3创建一个integer向量并c(1,2,3)创建一个numeric向量。您的示例中的函数需要f64,即numeric输入,因此不喜欢1:3(这将是i32)。您可以通过将函数参数设置为 来编写一个接受两者的函数Robj,例如
rextendr::rust_function(
"fn all_positive(input: Robj) -> extendr_api::scalar::Rbool {
let float_vec: Option<Vec<f64>> = input.as_real_vector();
let int_vec: Option<Vec<i32>> = input.as_integer_vector();
match float_vec {
Some(vec) => return extendr_api::scalar::Rbool::from_bool(vec.iter().all(|x: &f64| x > &0.0)),
None => (),
}
match int_vec {
Some(vec) => return extendr_api::scalar::Rbool::from_bool(vec.iter().all(|x: &i32| x > &0)),
None => (),
}
extendr_api::scalar::Rbool::na_value()
}
"
)
Run Code Online (Sandbox Code Playgroud)
编辑:现在返回一个 Rbool 而不是 Rust bool ,因此如果您使用非数字类型(例如字符向量)调用该函数,它可以返回 NA_logical_。
这就是 tl;dr。这是更多信息。这里有三个因素在起作用:
:符意味着 R 创建一个向量integer而不是一个numeric向量。f64值的数组切片。更一般地说,Rust 要求函数参数是显式类型,或者是具有共享特征的泛型。extendr 根据设计,不支持泛型。integervs numeric: a 有何:不同正如Dirk Eddelbuettel在评论中指出的那样,按照惯例:,运算符意味着一个整数向量(甚至在 之前ALTREP):
class(c(1,2,3)) # numeric
class(1:3) # integer
Run Code Online (Sandbox Code Playgroud)
我们可以使用该包查看底层的 C 表示lobstr:
lobstr::sxp(1:3)
# <INTSXP[3]> (altrep named:65535)
lobstr::sxp(c(1,2,3))
# <REALSXP[3]> (named:2)
Run Code Online (Sandbox Code Playgroud)
正如R 内部结构 中所述,anINTSXP是 C 值块int。AREALSXP是 C 值的块double。
INTSXP并REALSXP进入 Rust 世界我们可以使用extendr-api,extendr-engine和extendr-macroscrates ( v.0.4.0) 直接从 Rust 中查看如何使用宏extendr映射 R 类型:R!
fn main() {
test! {
let r_vec = R!("c(1,2,3)")?;
let r_altrep = R!("1:3")?;
println!("{}", Robj::is_altrep(&r_altrep)); // true
println!("{:?}", r_altrep); // [1,2,3]
println!("{:?}", r_vec); // [1.0, 2.0, 3.0]
}
}
Run Code Online (Sandbox Code Playgroud)
这种快速而肮脏的调试打印确认该1:3对象被打印为整数集合 ( i32),而c(1,2,3)打印为浮点数,即f64在 Rust 世界中。
现在我们知道我们正在处理不同的类型,我的诱惑是做类似的事情:
fn all_positive_iter<I>(r_obj: I) -> bool
where
I: IntoIterator,
{
// some code here
}
Run Code Online (Sandbox Code Playgroud)
然而,尽管这会在 Rust 中编译,但它不会编译为导出extendr函数。
integer创建一个接受并numeric输入类型的Rust 函数extendr关于缺乏泛型支持的 Github 问题建议创建一个 Rust 函数,该函数采用Robj,而不是原始 Rust 类型,并在 Rust 中解决这个问题。在这种情况下,我们可以使用Robj::as_integer_vector()和Robj::as_real_vector方法。
这些的文档似乎正在进行中,但我们可以从源代码中看到它们返回Option<T>类型,即Some(<T>)或None。我们可以使用该match构造尝试将从 R 接收到的内容转换为整数和浮点向量,并且仅在获取Some()类型时执行我们想要执行的操作。
顺便说一句,Rust 的一大优点是它与 R 非常相似,通常能够使用迭代器而不是循环。我想我们可以用你的代码的主要部分来做到这一点。我们可以使用与iter.all()文档非常相似的代码:
let a = [1, 2, 3];
assert!(a.iter().all(|&x| x > 0));
Run Code Online (Sandbox Code Playgroud)
如果您不想进行两次比较,您也可以键入cast the i32to ,尽管我们可以在我没有打扰的语句中链接the。f64.iter()match
1:3然后,这将允许我们调用在开始时为和定义的函数c(1,2,3)。
all_positive(c(1, 2, 3))
#> [1] TRUE
all_positive(c(0, 1, 2))
#> [1] FALSE
all_positive(1:3)
#> [1] TRUE
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
336 次 |
| 最近记录: |