1 个不稳定版本
0.0.1 | 2021 年 7 月 23 日 |
---|
#4 在 #extendr
42 每月下载
用于 5 个包 (4 个直接使用)
265KB
5.5K SLoC
extendr-api
安全且用户友好的 R 扩展接口。
- 构建 R 扩展到 Rust。
- 将 R 包转换为 Rust 包。
该库旨在为 Rust 或任何编译语言的新手用户提供一个熟悉的接口。
有关此包的大部分内容,请参阅 [Robj]。 [Robj] 为 R 对象类型提供了一个安全包装。
使用属性和宏导出到 R。
use extendr_api::prelude::*;
// Export a function or impl to R.
#[extendr]
fn fred(a: i32) -> i32 {
a + 1
}
// define exports using extendr_module
extendr_module! {
mod mymodule;
fn fred;
}
在 R 中
result <- fred(1)
[Robj] 是 R 对象的包装。 r!() 和 R!() 宏允许您使用 Rust 和 R 语法分别构建 R 对象。
use extendr_api::prelude::*;
test! {
// An R object with a single string "hello"
let character = r!("hello");
let character = r!(["hello", "goodbye"]);
// An R integer object with a single number 1L.
// Note that in Rust, 1 is an integer and 1.0 is a real.
let integer = r!(1);
// An R real object with a single number 1.
// Note that in R, 1 is a real and 1L is an integer.
let real = r!(1.0);
// An R real vector.
let real_vector = r!([1.0, 2.0]);
let real_vector = &[1.0, 2.0].iter().collect_robj();
let real_vector = r!(vec![1.0, 2.0]);
// An R function object.
let function = R!("function(x, y) { x + y }")?;
// A named list using the list! macro.
let list = list!(a = 1, b = 2);
// An unnamed list (of R objects) using the List wrapper.
let list = r!(List::from_values(vec![1, 2, 3]));
let list = r!(List::from_values(vec!["a", "b", "c"]));
let list = r!(List::from_values(&[r!("a"), r!(1), r!(2.0)]));
// A symbol
let sym = sym!(wombat);
// A R vector using collect_robj()
let vector = (0..3).map(|x| x * 10).collect_robj();
}
在 Rust 中,我们更愿意使用迭代器而不是循环。
use extendr_api::prelude::*;
test! {
// 1 ..= 100 is the same as 1:100
let res = r!(1 ..= 100);
assert_eq!(res, R!("1:100")?);
// Rust arrays are zero-indexed so it is more common to use 0 .. 100.
let res = r!(0 .. 100);
assert_eq!(res.len(), 100);
// Using map is a super fast way to generate vectors.
let iter = (0..3).map(|i| format!("fred{}", i));
let character = iter.collect_robj();
assert_eq!(character, r!(["fred0", "fred1", "fred2"]));
}
要索引向量,首先将其转换为切片,然后请记住使用基于 0 的索引。在 Rust 中,越界会导致错误(恐慌),而 C++ 可能会崩溃。
use extendr_api::prelude::*;
test! {
let vals = r!([1.0, 2.0]);
let slice = vals.as_real_slice().ok_or("expected slice")?;
let one = slice[0];
let two = slice[1];
// let error = slice[2];
assert_eq!(one, 1.0);
assert_eq!(two, 2.0);
}
这些方法速度较慢,但更通用
use extendr_api::prelude::*;
test! {
let vals = r!([1.0, 2.0, 3.0]);
// one-based indexing [[i]], returns an object.
assert_eq!(vals.index(1)?, r!(1.0));
// one-based slicing [x], returns an object.
assert_eq!(vals.slice(1..=2)?, r!([1.0, 2.0]));
// $ operator, returns an object
let list = list!(a = 1.0, b = "xyz");
assert_eq!(list.dollar("a")?, r!(1.0));
}
[R!] 宏允许您在 Rust 中嵌入 R 代码,并使用 {{ }} 对对 Rust 表达式。
[Rraw!] 宏不会展开 {{ }} 对。
use extendr_api::prelude::*;
test! {
// The text "1 + 1" is parsed as R source code.
// The result is 1.0 + 1.0 in Rust.
assert_eq!(R!("1 + 1")?, r!(2.0));
let a = 1.0;
assert_eq!(R!("1 + {{a}}")?, r!(2.0));
assert_eq!(R!(r"
x <- {{ a }}
x + 1
")?, r!(2.0));
assert_eq!(R!(r#"
x <- "hello"
x
"#)?, r!("hello"));
// Use the R meaning of {{ }} and do not expand.
assert_eq!(Rraw!(r"
x <- {{ 1 }}
x + 1
")?, r!(2.0));
}
[r!] 宏将 Rust 对象转换为 R 对象,并接受参数。
use extendr_api::prelude::*;
test! {
// The text "1.0+1.0" is parsed as Rust source code.
let one = 1.0;
assert_eq!(r!(one+1.0), r!(2.0));
}
您可以使用 [call!] 宏调用 R 函数和原语。
use extendr_api::prelude::*;
test! {
// As one R! macro call
let confint1 = R!(confint(lm(weight ~ group - 1, PlantGrowth)))?;
// As many parameterized calls.
let formula = call!("~", sym!(weight), lang!("-", sym!(group), 1))?;
let plant_growth = global!(PlantGrowth)?;
let model = call!("lm", formula, plant_growth)?;
let confint2 = call!("confint", model)?;
assert_eq!(confint1.as_real_vector(), confint2.as_real_vector());
}
Rust 有“所有者”和“借用”对象的概念。
所有者对象,如 [Vec] 和 [String],在对象生命周期结束时释放内存。
借用对象,如 &[i32] 和 &str,只是指向另一个对象内存的指针,其生命周期不能超过所引用的对象。
借用对象比所有者对象快得多,并且占用更少的内存,但仅用于临时访问。
例如,当我们从 R 向量中获取切片时,需要原始 R 对象保持活动状态,否则数据将被损坏。
use extendr_api::prelude::*;
test! {
// robj is an "Owned" object that controls the memory allocated.
let robj = r!([1, 2, 3]);
// Here slice is a "borrowed" reference to the bytes in robj.
// and cannot live longer than robj.
let slice = robj.as_integer_slice().ok_or("expected slice")?;
assert_eq!(slice.len(), 3);
}
许可证:MIT
依赖关系
~5.5MB
~135K SLoC