#iterator #generics #function #ndarray #macro #array-string

anyinput

一个宏,用于简化编写接受任何字符串、路径、迭代器、数组或ndarray-like输入的函数

7 个版本

0.1.6 2022 年 10 月 20 日
0.1.5 2022 年 10 月 11 日

#895Rust 模式

Download history 80/week @ 2024-03-13 81/week @ 2024-03-20 76/week @ 2024-03-27 66/week @ 2024-04-03 115/week @ 2024-04-10 225/week @ 2024-04-17 227/week @ 2024-04-24 71/week @ 2024-05-01 42/week @ 2024-05-08 105/week @ 2024-05-15 114/week @ 2024-05-22 62/week @ 2024-05-29 53/week @ 2024-06-05 67/week @ 2024-06-12 54/week @ 2024-06-19 354/week @ 2024-06-26

540 每月下载次数
用于 5 个crate(2 个直接使用)

MIT/Apache

14KB

anyinput

github crates.io docs.rsCI

一个宏,用于简化编写接受任何字符串、路径、迭代器、数组或ndarray-like输入的函数

你知道如何编写一个接受任何类型字符串作为输入的函数吗?(你确定它接受,例如,对 String 的借用引用吗?)你知道如何接受任何类型的迭代器吗?一个路径类型的迭代器呢?一个接受 Vec<f32> 作为 ndarray::ArrayView1 的函数呢?如果是,你不需要这个crate。

Rust 函数可以接受所有这些输入,但语法可能难以记住和阅读。此crate提供了一些语法糖,使得编写和阅读此类函数更容易。

AnyInputs 包括 AnyStringAnyPathAnyIterAnyArray 以及(可选的)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 并返回平均值。我们将该函数应用于 VecArray1<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