gpt4 book ai didi

rust - 如何在 Rust 中实现带量纲的标量算法?

转载 作者:行者123 更新时间:2023-12-05 03:20:46 25 4
gpt4 key购买 nike

Rust 初学者。

当尝试实现(仅用于学习)具有四种基本算术的维度标量系统时,即实现如下目标:

fn main() {
let length1 = 3.meters();
// ^^^^^^ to be implemented
let length2 = 4.meters();
let area = length1 * length2;
println!("{:?}", area); // maybe something like DimensionedScalar{value: 12, unit: {meter: 2}}, where {meter: 2} means meter-square

let temperature = 243.kelvins();
// ^^^^^^^ to be implemented
let dilation_rate = 3.2.meters() * 1.meters() * 1.meters() / 1.kelvins();
let non_sensical = temperature + dilation_rate; // should raise error because of dimensionality mismatch
}

,我遇到了维度检查的问题:

如果维数检查发生在编译时

在这种情况下,我们似乎需要一个依赖类型,因为单位的指数可以是任意数字。

维数检查发生在运行时

然后它需要:

  • 运算符(+-)返回 Result 类型而不是标量类型,这对于当前的 来说似乎是不可能的>std::ops::{ Add, Sub } 特征定义,或
  • 当检查失败时 panic ,作为解决方案并不令人满意。
当然还有一个不太酷的方案

最终,我当然可以放弃 +- 的原生语法并定义我自己的方法,例如 subtract()add_with() 这样我就可以返回一个 Result 类型。但这无疑会不那么酷。

有什么建议吗?

最佳答案

一般技术是将 n 维类型代数视为 ℤn 中的向量空间,其中每个向量系数对应于单位的指数。例如,这个向量空间的“基础”可以是 SI 单位。那么单位的乘法在这个向量空间就变成了加法,除法就变成了减法。您可以将向量空间的系数存储为模板参数,这样可以确保在具有相同单位的值之间实现加法/减法。为了帮助解决这个问题,我建议使用 typenum箱。最后,您可以添加类型别名以使其更易于使用。

仅以 MeterKelvinSecond 为例,使用 f64 作为存储:

use core::marker::PhantomData;
use typenum::{Integer, Z0, P1};

#[derive(Clone, Copy)]
struct SIValue<M, K, S> {
val: f64,
unit: PhantomData<(M, K, S)>,
}

type Scalar = SIValue<Z0, Z0, Z0>; // Unitless - zero vector.
type Meter = SIValue<P1, Z0, Z0>; // Basis type vector for meter.
type Kelvin = SIValue<Z0, P1, Z0>; // Basis type vector for Kelvin.
type Second = SIValue<Z0, Z0, P1>; // Basis type vector for second.

fn main() {
let g = Meter::new(9.8) / Second::new(1.0) / Second::new(1.0);
println!("Gravitional constant: {}", g);

let mut dist = Meter::new(0.0);
let mut accel = Meter::new(0.0) / Second::new(1.0);
let mut t = Second::new(0.0);
let dt = Second::new(0.001);
let maxt = Second::new(5.0);
while t < maxt {
accel = accel + dt * g;
dist = dist + accel * dt;
t = t + dt;
}
println!("Distance after falling for {t:.1}: {dist:.1}");
println!("Acceleration after {t:.1}: {accel:.1}");
}

打印:

Gravitional constant: 9.8 m s^-2
Distance after falling for 5.0 s: 122.5 m
Acceleration after 5.0 s: 49.0 m s^-1

给定以下实现(或在 playground 中):

use core::ops::{Add, Sub, Mul, Div};
use core::fmt;

impl<M, K, S> SIValue<M, K, S> {
fn new(val: f64) -> Self {
Self { val, unit: PhantomData }
}
}

impl<M: Integer, K: Integer, S: Integer> fmt::Display for SIValue<M, K, S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let write_unit = |f: &mut fmt::Formatter, unit, exp| {
match exp {
0 => Ok(()),
1 => write!(f, " {}", unit),
n => write!(f, " {}^{}", unit, exp),
}
};

write!(f, "{}", self.val)?;
write_unit(f, "m", M::to_i32())?;
write_unit(f, "K", K::to_i32())?;
write_unit(f, "s", S::to_i32())
}
}

impl<M, K, S> Add for SIValue<M, K, S> {
type Output = Self;

fn add(self, rhs: SIValue<M, K, S>) -> Self::Output {
SIValue::new(self.val + rhs.val)
}
}

impl<M, K, S> Sub for SIValue<M, K, S> {
type Output = Self;

fn sub(self, rhs: SIValue<M, K, S>) -> Self::Output {
SIValue::new(self.val - rhs.val)
}
}

impl<LM, LK, LS, RM, RK, RS> Mul<SIValue<RM, RK, RS>> for SIValue<LM, LK, LS>
where
LM: Add<RM>, LK: Add<RK>, LS: Add<RS>,
{
type Output = SIValue<
<LM as Add<RM>>::Output,
<LK as Add<RK>>::Output,
<LS as Add<RS>>::Output,
>;

fn mul(self, rhs: SIValue<RM, RK, RS>) -> Self::Output {
SIValue::new(self.val * rhs.val)
}
}

impl<LM, LK, LS, RM, RK, RS> Div<SIValue<RM, RK, RS>> for SIValue<LM, LK, LS>
where
LM: Sub<RM>, LK: Sub<RK>, LS: Sub<RS>,
{
type Output = SIValue<
<LM as Sub<RM>>::Output,
<LK as Sub<RK>>::Output,
<LS as Sub<RS>>::Output,
>;

fn div(self, rhs: SIValue<RM, RK, RS>) -> Self::Output {
SIValue::new(self.val / rhs.val)
}
}

如果你试图添加错误的单位,你会得到一个错误,就像你在上面的例子中做 accel + dt 一样:

error[E0308]: mismatched types
--> src/main.rs:17:25
|
17 | accel = accel + dt;
| ^^ expected struct `PInt`, found struct `Z0`
|
= note: expected struct `SIValue<PInt<UInt<UTerm, B1>>, _, NInt<UInt<UTerm, B1>>>`
found struct `SIValue<Z0, _, PInt<UInt<UTerm, B1>>>`

不幸的是,错误消息不是特别好。

关于rust - 如何在 Rust 中实现带量纲的标量算法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73091949/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com