7 个版本
0.1.6 | 2022 年 10 月 20 日 |
---|---|
0.1.5 | 2022 年 10 月 11 日 |
#895 在 Rust 模式
540 每月下载次数
用于 5 个crate(2 个直接使用)
14KB
anyinput
一个宏,用于简化编写接受任何字符串、路径、迭代器、数组或ndarray-like输入的函数
你知道如何编写一个接受任何类型字符串作为输入的函数吗?(你确定它接受,例如,对 String
的借用引用吗?)你知道如何接受任何类型的迭代器吗?一个路径类型的迭代器呢?一个接受 Vec<f32>
作为 ndarray::ArrayView1
的函数呢?如果是,你不需要这个crate。
Rust 函数可以接受所有这些输入,但语法可能难以记住和阅读。此crate提供了一些语法糖,使得编写和阅读此类函数更容易。
AnyInputs 包括 AnyString
、AnyPath
、AnyIter
、AnyArray
以及(可选的)AnyNdArray
。它们可以是嵌套的。
内容
用法
将其添加到您的 Cargo.toml
[dependencies]
anyinput = { version = "0.1.6", features = ["ndarray"] }
如果您不需要 NdArray
支持,请省略 ndarray
功能。
示例
我们将从一些非常简单的示例开始,这些示例可能不需要宏。我们想展示简单的示例仍然保持简单。
创建一个函数,将 2
添加到任何类似字符串的长度。
use anyinput::anyinput;
#[anyinput]
fn len_plus_2(s: AnyString) -> usize {
s.len()+2
}
// By using AnyString, len_plus_2 works with
// &str, String, or &String -- borrowed or moved.
assert_eq!(len_plus_2("Hello"), 7); // move a &str
let input: &str = "Hello";
assert_eq!(len_plus_2(&input), 7); // borrow a &str
let input: String = "Hello".to_string();
assert_eq!(len_plus_2(&input), 7); // borrow a String
let input2: &String = &input;
assert_eq!(len_plus_2(&input2), 7); // borrow a &String
assert_eq!(len_plus_2(input2), 7); // move a &String
assert_eq!(len_plus_2(input), 7); // move a String
另一个简单的示例:创建一个函数,统计任何类似路径的组件数量。
use anyinput::anyinput;
use std::path::Path;
#[anyinput]
fn component_count(path: AnyPath) -> usize {
path.iter().count()
}
// By using AnyPath, component_count works with any
// string-like or path-like thing, borrowed or moved.
assert_eq!(component_count("usr/files/home"), 3);
let path = Path::new("usr/files/home");
assert_eq!(component_count(&path), 3);
let pathbuf = path.to_path_buf();
assert_eq!(component_count(pathbuf), 3);
随着嵌套和多个输入的增加,宏变得更加有用。这里我们创建了一个具有两个输入的函数。一个输入接受任何类似迭代器的 usize
类型的迭代器。第二个输入接受任何类似字符串的迭代器的迭代器。函数返回数字和字符串长度的总和。
我们将此函数应用于 1..=10
的范围和一个 &str
切片的迭代器。
use anyinput::anyinput;
#[anyinput]
fn two_iterator_sum(iter1: AnyIter<usize>, iter2: AnyIter<AnyString>) -> usize {
let mut sum = iter1.sum();
for any_string in iter2 {
// Needs .as_ref to turn the nested AnyString into a &str.
sum += any_string.as_ref().len();
}
sum
}
assert_eq!(two_iterator_sum(1..=10, ["a", "bb", "ccc"]), 61);
创建一个函数,该函数接受类似路径的类似数组的对象。返回索引处的路径组件数。
use anyinput::anyinput;
use anyhow::Result;
#[anyinput]
fn indexed_component_count(
array: AnyArray<AnyPath>,
index: usize,
) -> Result<usize, anyhow::Error> {
// Needs .as_ref to turn the nested AnyPath into a &Path.
let path = array[index].as_ref();
let count = path.iter().count();
Ok(count)
}
assert_eq!(
indexed_component_count(vec!["usr/files/home", "usr/data"], 1)?,
2
);
# // '# OK...' needed for doctest
# Ok::<(), anyhow::Error>(())
您可以将 NdArray
函数轻松应用于任何类似数字的类似数组对象。例如,这里我们创建了一个函数,该函数接受类似 NdArray
的对象,其中包含 f32
并返回平均值。我们将该函数应用于 Vec
和 Array1<f32>
。
NdArray
的支持由可选功能 ndarray
提供。
use anyinput::anyinput;
use anyhow::Result;
# // '#[cfg...' needed for doctest
# #[cfg(feature = "ndarray")]
#[anyinput]
fn any_mean(array: AnyNdArray<f32>) -> Result<f32, anyhow::Error> {
if let Some(mean) = array.mean() {
Ok(mean)
} else {
Err(anyhow::anyhow!("empty array"))
}
}
// 'AnyNdArray' works with any 1-D array-like thing, but must be borrowed.
# #[cfg(feature = "ndarray")]
assert_eq!(any_mean(&vec![10.0, 20.0, 30.0, 40.0])?, 25.0);
# #[cfg(feature = "ndarray")]
assert_eq!(any_mean(&ndarray::array![10.0, 20.0, 30.0, 40.0])?, 25.0);
# // '# OK...' needed for doctest
# Ok::<(), anyhow::Error>(())
AnyInputs
任何输入 | 描述 | 创建具体类型 |
---|---|---|
任何字符串 | 任何类似字符串的对象 | &str |
任何路径 | 任何路径类似或字符串类似的对象 | &路径 |
任何迭代器 | 任何类似迭代器的对象 | <I作为 IntoIterator>::IntoIter |
任何数组 | 任何类似数组的对象 | &[T] |
任何 NdArray | 任何 1-D 类似数组的对象(仅借用) | ndarray::ArrayView1<T> |
注释 & 特性
-
欢迎提出建议、功能请求和贡献。
-
支持嵌套、多个输入和泛型。
-
自动且高效地将顶级 AnyInput 转换为具体类型。
-
AnyArray、AnyIter 和 AnyNdArray 的元素必须是单一类型。因此,
AnyArray<AnyString>
接受所有&str
或所有String
的向量,但不能混合。 -
在嵌套时,使用以下方法高效地将嵌套的 AnyInput 转换为具体类型:
.as_ref()
-- AnyString,AnyPath,AnyArray.into_iter()
-- AnyIter.into()
-- AnyNdArray
(上面的迭代器和数组示例展示了这一点。)
-
让您轻松地将
NdArray
函数应用于常规 Rust 数组、切片和Vec
。 -
由 bed-reader(基因组 crate)和 fetch-data(样本文件下载 crate) 使用。
工作原理
#[anyinput]
宏使用标准的 Rust 泛型来支持多种输入类型。为此,它使用适当的泛型重写您的函数。它还在您的函数中添加了行,以有效地将任何顶级泛型转换为具体类型。例如,该宏将 len_plus_2
从
use anyinput::anyinput;
#[anyinput]
fn len_plus_2(s: AnyString) -> usize {
s.len()+2
}
转换为
fn len_plus_2<AnyString0: AsRef<str>>(s: AnyString0) -> usize {
let s = s.as_ref();
s.len() + 2
}
在这里,AnyString0
是泛型类型。行 let s = s.as_ref()
将泛型类型 AnyString0
转换为具体类型 &str
。
与所有 Rust 泛型一样,编译器为调用代码使用的每个具体类型的组合创建一个单独的函数。
项目链接
依赖项
~1.2–1.9MB
~42K SLoC