3 个不稳定版本
0.2.0 | 2021 年 7 月 6 日 |
---|---|
0.1.1 | 2019 年 11 月 26 日 |
0.1.0 | 2019 年 5 月 19 日 |
在 Rust 模式 中排名 1761
每月下载量 389
在 17 个 包中使用(直接使用 10 个)
24KB
289 行代码(不包括注释)
numeric_literals
numeric_literals 是一个 Rust 库,它提供过程式属性宏,用于将数字字面量替换为任意表达式。
虽然 Rust 的显式性通常是一件好事,但当编写泛型标量类型的数字代码时,它却是一个大问题。例如,考虑编写一个函数,该函数返回任何实现 T: num::Float
的类型的黄金比例。一个实现可能看起来如下所示。
extern crate num;
use num::Float;
fn golden_ratio<T: Float>() -> T {
( T::one() + T::sqrt(T::from(5).unwrap())) / T::from(2).unwrap()
}
对于这样一个简单的任务来说,这无疑是相当混乱的。使用 numeric_literals
,我们可以改写为
use num::Float;
use numeric_literals::replace_numeric_literals;
#[replace_numeric_literals(T::from(literal).unwrap())]
fn golden_ratio<T: Float>() -> T {
(1 + 5.sqrt()) / 2
}
上述两个代码段基本上执行相同的功能(除了使用 T::from(1)
而不是 T::one()
)。然而,在后者中,replace_numeric_literals
属性将任何数字字面量替换为表达式 T::from(literal).unwrap()
,其中 literal
是每个单个字面量的占位符。
没有魔法:代码仍然明确说明了它对数字字面量做了什么。区别在于,我们可以为所有数字字面量声明一次这种行为。此外,我们将转换行为从需要字面量的地方移开,通过减少对确切类型明确性的噪音,提高了可读性。
浮点数和整数字面量替换
在替换数字字面量时的问题在于,无法区分用于索引等目的的字面量与属于数值计算的字面量。在上面的例子中,如果你还需要使用常量索引(如 array[0]
)来索引数组,宏会尝试将索引 0
转换为浮点类型,这显然会失败。幸运的是,在大多数情况下,这些例子会因为类型不匹配而无法编译。解决这个问题的可能方法之一是使用独立的宏 replace_float_literals
和 replace_int_literals
,这两个宏的工作方式完全相同,但分别只触发于浮点或整数字面量。下面是一个来自有限元代码的例子,它使用浮点字面量替换来改善通用代码中数值常量的可读性。
#[replace_float_literals(T::from_f64(literal).expect("Literal must fit in T"))]
pub fn assemble_element_mass<T>(quad: &Quad2d<T>) -> MatrixN<T, U8>
where
T: RealField
{
let phi = |alpha, beta, xi: &Vector2<T>| -(1.0 + alpha * xi[0]) * (1.0 + beta * xi[1]) / 4.0;
let phi_grad = |alpha, beta, xi: &Vector2<T>| {
Vector2::new(
alpha * (1.0 + beta * xi[1]) / 4.0,
beta * (1.0 + alpha * xi[0]) / 4.0,
)
};
let alphas = [-1.0, 1.0, 1.0, -1.0];
let betas = [-1.0, -1.0, 1.0, 1.0];
// And so on...
}
一般情况下,应该 谨慎使用宏。建议将宏与使用字面量的代码区域保持接近,以避免让代码读者产生困惑。宏展开前的Rust代码通常不是有效的Rust代码(因为缺乏显式的类型转换),但没有属性上下文,代码仍然不清楚为什么它仍然可以编译。未来的一个选项是将属性仅应用于非常局部的、数值常数较多的代码块。然而,目前Rust不允许属性宏应用于块或单个表达式。
宏调用中的替换
默认情况下,这个crate的宏也会替换宏调用中的字面量。这使得以下代码可以编译
use num::Float;
use numeric_literals::replace_numeric_literals;
#[replace_numeric_literals(T::from(literal).unwrap())]
fn zeros<T: Float>(n: usize) -> Vec<T> {
vec![0.0; n]
}
如果这种行为不想要,可以通过参数禁用在宏内部进行替换
#[replace_numeric_literals(T::from(literal).unwrap()), visit_macros = false]
带后缀的字面量
在Rust中,可以使用字面量后缀来区分字面量的类型。例如,表达式 1_f64.sqrt()
中的后缀 _f64
明确表示值 1
的类型是 f64
。这个crate的宏也支持所有浮点数和整数后缀。例如
use num::Float;
use numeric_literals::replace_numeric_literals;
#[replace_numeric_literals(T::from(literal).unwrap())]
fn golden_ratio<T: Float>() -> T {
(1.0_f64 + 5f32.sqrt()) / 2.0
}
许可证
此crate采用MIT许可证。有关详细信息,请参阅LICENSE。
依赖项
~1.5MB
~36K SLoC